diff --git a/.github/workflows/block-remove-before-merge.yml b/.github/workflows/block-remove-before-merge.yml index 443eea9f7c..0cd4a9824e 100644 --- a/.github/workflows/block-remove-before-merge.yml +++ b/.github/workflows/block-remove-before-merge.yml @@ -17,7 +17,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - timeout-minutes: 4 with: fetch-depth: 2 - name: Check for remove-before-merge paths in PR diff --git a/.github/workflows/java-publish-snapshot.yml b/.github/workflows/java-publish-snapshot.yml index 77d5287964..219dd43bf2 100644 --- a/.github/workflows/java-publish-snapshot.yml +++ b/.github/workflows/java-publish-snapshot.yml @@ -370,7 +370,7 @@ jobs: build-darwin-classifier: name: Build Darwin snapshot classifier needs: resolve-source - runs-on: macos-26 + runs-on: macos-26-agent-runtime permissions: contents: read outputs: diff --git a/.github/workflows/sdk-dotnet.yml b/.github/workflows/sdk-dotnet.yml index 63b689f94e..5ebbe5c0c4 100644 --- a/.github/workflows/sdk-dotnet.yml +++ b/.github/workflows/sdk-dotnet.yml @@ -7,7 +7,6 @@ env: HUSKY: 0 POWERSHELL_UPDATECHECK: Off SDK_HOME: ${{ inputs.sdk-home }} - SETUP_NODE_TIMEOUT_MINUTES: 10 on: workflow_call: @@ -27,7 +26,6 @@ jobs: dotnet-linux-x64: name: ".NET (ubuntu-latest, CAPI)" if: inputs.platform == 'all' || inputs.platform == 'linux-x64' - timeout-minutes: 30 runs-on: ubuntu-latest defaults: run: @@ -38,7 +36,6 @@ jobs: DOTNET_TEST_RESULTS_DIRECTORY: TestResults/subprocess steps: - uses: actions/checkout@v7 - timeout-minutes: 4 with: persist-credentials: false - uses: ./.github/actions/download-sdk-runtime @@ -99,10 +96,9 @@ jobs: retention-days: 7 dotnet-darwin-arm64: - name: ".NET (macos-26-xlarge, default, CAPI)" + name: ".NET (macos-26-xlarge-agent-runtime, default, CAPI)" if: inputs.platform == 'all' || inputs.platform == 'darwin-arm64' - runs-on: macos-26-xlarge - timeout-minutes: 30 + runs-on: macos-26-xlarge-agent-runtime defaults: run: shell: bash @@ -112,7 +108,6 @@ jobs: DOTNET_TEST_RESULTS_DIRECTORY: TestResults/subprocess steps: - uses: actions/checkout@v7 - timeout-minutes: 4 with: persist-credentials: false - uses: ./.github/actions/download-sdk-runtime @@ -153,10 +148,19 @@ jobs: retention-days: 7 dotnet-win32-x64: - name: ".NET (windows-latest, default, CAPI)" + name: ".NET (windows-latest, ${{ matrix.framework }}, ${{ matrix.suite.name }}, CAPI)" if: inputs.platform == 'all' || inputs.platform == 'win32-x64' runs-on: windows-latest - timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + framework: [net8.0, net472] + # Complementary filters include every test, including newly added classes. + suite: + - name: e2e-s + filter: FullyQualifiedName~GitHub.Copilot.Test.E2E.S + - name: remaining + filter: FullyQualifiedName!~GitHub.Copilot.Test.E2E.S defaults: run: shell: bash @@ -164,10 +168,11 @@ jobs: env: COPILOT_SDK_E2E_BACKEND: capi DOTNET_TEST_RUNTIME: win-x64 + DOTNET_TEST_FRAMEWORK: ${{ matrix.framework }} + DOTNET_TEST_FILTER: ${{ matrix.suite.filter }} DOTNET_TEST_RESULTS_DIRECTORY: TestResults/subprocess steps: - uses: actions/checkout@v7 - timeout-minutes: 4 with: persist-credentials: false - uses: ./.github/actions/download-sdk-runtime @@ -183,7 +188,7 @@ jobs: with: node-version: 22 - run: dotnet restore test/GitHub.Copilot.SDK.Test.csproj --runtime "$DOTNET_TEST_RUNTIME" -p:CopilotCliBinaryPath="$COPILOT_RUNTIME_BINARY_PATH" -p:CopilotSkipCliDownload=true - - run: dotnet build test/GitHub.Copilot.SDK.Test.csproj --no-restore --runtime "$DOTNET_TEST_RUNTIME" -p:CopilotCliBinaryPath="$COPILOT_RUNTIME_BINARY_PATH" -p:CopilotSkipCliDownload=true + - run: dotnet build test/GitHub.Copilot.SDK.Test.csproj --no-restore --framework "$DOTNET_TEST_FRAMEWORK" --runtime "$DOTNET_TEST_RUNTIME" -p:CopilotCliBinaryPath="$COPILOT_RUNTIME_BINARY_PATH" -p:CopilotSkipCliDownload=true - run: npm ci --ignore-scripts working-directory: ${{ inputs.sdk-home }}/test/harness - run: pwsh.exe -Command "Write-Host 'PowerShell ready'" @@ -203,7 +208,7 @@ jobs: - if: failure() uses: actions/upload-artifact@v7 with: - name: dotnet-test-diagnostics-windows-latest-default-capi-${{ github.run_attempt }} + name: dotnet-test-diagnostics-windows-latest-${{ matrix.framework }}-${{ matrix.suite.name }}-capi-${{ github.run_attempt }} path: ${{ inputs.sdk-home }}/dotnet/TestResults/ if-no-files-found: warn retention-days: 7 @@ -212,12 +217,10 @@ jobs: name: ".NET (Alpine x64, CAPI)" if: inputs.platform == 'all' || inputs.platform == 'linuxmusl-x64' runs-on: ubuntu-latest - timeout-minutes: 20 env: COPILOT_SDK_E2E_BACKEND: capi steps: - uses: actions/checkout@v7 - timeout-minutes: 4 with: persist-credentials: false - uses: ./.github/actions/download-sdk-runtime diff --git a/.github/workflows/sdk-go.yml b/.github/workflows/sdk-go.yml index e7235998f8..d07042076e 100644 --- a/.github/workflows/sdk-go.yml +++ b/.github/workflows/sdk-go.yml @@ -7,7 +7,6 @@ env: HUSKY: 0 POWERSHELL_UPDATECHECK: Off SDK_HOME: ${{ inputs.sdk-home }} - SETUP_NODE_TIMEOUT_MINUTES: 10 on: workflow_call: @@ -30,7 +29,7 @@ jobs: strategy: fail-fast: false matrix: - os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-latest"]' || inputs.platform == 'darwin-arm64' && '["macos-26"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-26","windows-latest"]') }} + os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-latest"]' || inputs.platform == 'darwin-arm64' && '["macos-26-agent-runtime"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-26-agent-runtime","windows-latest"]') }} runs-on: ${{ matrix.os }} defaults: run: @@ -38,7 +37,6 @@ jobs: working-directory: ${{ inputs.sdk-home }}/go steps: - uses: actions/checkout@v7 - timeout-minutes: 4 with: persist-credentials: false - uses: ./.github/actions/download-sdk-runtime @@ -68,7 +66,9 @@ jobs: - if: runner.os == 'Linux' run: go test ./... working-directory: ${{ inputs.sdk-home }}/go/samples - - run: npm ci --ignore-scripts + - run: | + npm ci --ignore-scripts + npm rebuild esbuild working-directory: ${{ inputs.sdk-home }}/test/harness - if: runner.os == 'Windows' run: pwsh.exe -Command "Write-Host 'PowerShell ready'" @@ -102,10 +102,8 @@ jobs: name: "Go (Alpine x64)" if: inputs.platform == 'all' || inputs.platform == 'linuxmusl-x64' runs-on: ubuntu-latest - timeout-minutes: 20 steps: - uses: actions/checkout@v7 - timeout-minutes: 4 with: persist-credentials: false - uses: ./.github/actions/download-sdk-runtime @@ -125,6 +123,7 @@ jobs: apk add --no-cache build-base nodejs npm npm --prefix "$COPILOT_SDK_ROOT/nodejs" ci --ignore-scripts npm --prefix "$COPILOT_SDK_ROOT/test/harness" ci --ignore-scripts + npm --prefix "$COPILOT_SDK_ROOT/test/harness" rebuild esbuild export GITHUB_ENV=/tmp/copilot-sdk-runtime.env export COPILOT_RUNTIME_TARGET=linuxmusl-x64 diff --git a/.github/workflows/sdk-java.yml b/.github/workflows/sdk-java.yml index fbadf275ce..28f9650925 100644 --- a/.github/workflows/sdk-java.yml +++ b/.github/workflows/sdk-java.yml @@ -6,7 +6,6 @@ env: HUSKY: 0 POWERSHELL_UPDATECHECK: Off SDK_HOME: ${{ inputs.sdk-home }} - SETUP_NODE_TIMEOUT_MINUTES: 10 on: workflow_call: @@ -29,10 +28,10 @@ jobs: strategy: fail-fast: false matrix: - os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-latest"]' || inputs.platform == 'darwin-arm64' && '["macos-26"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-26","windows-latest"]') }} + os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-latest"]' || inputs.platform == 'darwin-arm64' && '["macos-26-agent-runtime"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-26-agent-runtime","windows-latest"]') }} test-jdk: ["25", "17"] exclude: - - os: macos-26 + - os: macos-26-agent-runtime test-jdk: "17" - os: windows-latest test-jdk: "17" @@ -43,7 +42,6 @@ jobs: working-directory: ${{ inputs.sdk-home }}/java steps: - uses: actions/checkout@v7 - timeout-minutes: 4 with: persist-credentials: false - uses: ./.github/actions/download-sdk-runtime @@ -126,10 +124,8 @@ jobs: name: "Java (JDK 25, linuxmusl-x64)" if: inputs.platform == 'all' || inputs.platform == 'linuxmusl-x64' runs-on: ubuntu-latest - timeout-minutes: 30 steps: - uses: actions/checkout@v7 - timeout-minutes: 4 with: persist-credentials: false - uses: ./.github/actions/download-sdk-runtime @@ -187,10 +183,8 @@ jobs: name: "Java (JDK 25, linuxmusl-arm64)" if: inputs.platform == 'all' || inputs.platform == 'linuxmusl-arm64' runs-on: ubuntu-24.04-arm - timeout-minutes: 30 steps: - uses: actions/checkout@v7 - timeout-minutes: 4 with: persist-credentials: false - uses: ./.github/actions/download-sdk-runtime diff --git a/.github/workflows/sdk-nodejs.yml b/.github/workflows/sdk-nodejs.yml index 9fbbf97e76..d573049446 100644 --- a/.github/workflows/sdk-nodejs.yml +++ b/.github/workflows/sdk-nodejs.yml @@ -7,7 +7,6 @@ env: HUSKY: 0 POWERSHELL_UPDATECHECK: Off SDK_HOME: ${{ inputs.sdk-home }} - SETUP_NODE_TIMEOUT_MINUTES: 10 on: workflow_call: @@ -35,7 +34,7 @@ jobs: strategy: fail-fast: false matrix: - os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-latest"]' || inputs.platform == 'darwin-arm64' && '["macos-26"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-26","windows-latest"]') }} + os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-latest"]' || inputs.platform == 'darwin-arm64' && '["macos-26-agent-runtime"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-26-agent-runtime","windows-latest"]') }} runs-on: ${{ matrix.os }} defaults: run: @@ -43,7 +42,6 @@ jobs: working-directory: ${{ inputs.sdk-home }}/nodejs steps: - uses: actions/checkout@v7 - timeout-minutes: 4 with: persist-credentials: false - uses: ./.github/actions/download-sdk-runtime @@ -94,17 +92,17 @@ jobs: env: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} run: | - if [ "$GITHUB_EVENT_NAME" = "merge_group" ] || [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then + if [ "$GITHUB_EVENT_NAME" = "merge_group" ] || [ "$GITHUB_EVENT_NAME" = "pull_request" ] || [ "$GITHUB_EVENT_NAME" = "push" ]; then npm test -- --reporter=default --reporter=json --outputFile="$RUNNER_TEMP/sdk-nodejs-results.json" else npm test fi - name: Upload Flake Finder Node.js test results - if: always() && (github.event_name == 'merge_group' || github.event_name == 'pull_request') && runner.os == 'Linux' + if: always() && (github.event_name == 'merge_group' || github.event_name == 'pull_request' || github.event_name == 'push') continue-on-error: true uses: actions/upload-artifact@v7 with: - name: suspected-flakes-sdk-nodejs-${{ github.run_attempt }}-linux + name: suspected-flakes-sdk-nodejs-${{ github.run_attempt }}-${{ runner.os == 'macOS' && 'macos' || runner.os == 'Windows' && 'windows' || 'linux' }} path: ${{ runner.temp }}/sdk-nodejs-results.json if-no-files-found: warn retention-days: 7 @@ -128,7 +126,6 @@ jobs: name: "Node.js (ubuntu-latest, ${{ matrix.backend }})" if: github.event_name != 'merge_group' && (inputs.platform == 'all' || inputs.platform == 'linux-x64') runs-on: ubuntu-latest - timeout-minutes: 30 strategy: fail-fast: false matrix: @@ -141,7 +138,6 @@ jobs: COPILOT_SDK_E2E_BACKEND: ${{ matrix.backend }} steps: - uses: actions/checkout@v7 - timeout-minutes: 4 with: persist-credentials: false - uses: ./.github/actions/download-sdk-runtime @@ -170,7 +166,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - timeout-minutes: 4 with: persist-credentials: false - uses: ./.github/actions/download-sdk-runtime diff --git a/.github/workflows/sdk-python.yml b/.github/workflows/sdk-python.yml index 2abeca3d8a..b2db80794b 100644 --- a/.github/workflows/sdk-python.yml +++ b/.github/workflows/sdk-python.yml @@ -7,7 +7,6 @@ env: HUSKY: 0 POWERSHELL_UPDATECHECK: Off SDK_HOME: ${{ inputs.sdk-home }} - SETUP_NODE_TIMEOUT_MINUTES: 10 on: workflow_call: @@ -30,9 +29,8 @@ jobs: strategy: fail-fast: false matrix: - os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-latest"]' || inputs.platform == 'darwin-arm64' && '["macos-26"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-26","windows-latest"]') }} + os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-latest"]' || inputs.platform == 'darwin-arm64' && '["macos-26-agent-runtime"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-26-agent-runtime","windows-latest"]') }} runs-on: ${{ matrix.os }} - timeout-minutes: 20 defaults: run: shell: bash @@ -41,7 +39,6 @@ jobs: PYTHONUTF8: 1 steps: - uses: actions/checkout@v7 - timeout-minutes: 4 with: persist-credentials: false - uses: ./.github/actions/download-sdk-runtime @@ -105,10 +102,8 @@ jobs: name: "Python (Alpine x64)" if: inputs.platform == 'all' || inputs.platform == 'linuxmusl-x64' runs-on: ubuntu-latest - timeout-minutes: 20 steps: - uses: actions/checkout@v7 - timeout-minutes: 4 with: persist-credentials: false - uses: ./.github/actions/download-sdk-runtime diff --git a/.github/workflows/sdk-runtime-artifact.yml b/.github/workflows/sdk-runtime-artifact.yml index 0481e945ae..51c226b429 100644 --- a/.github/workflows/sdk-runtime-artifact.yml +++ b/.github/workflows/sdk-runtime-artifact.yml @@ -11,7 +11,6 @@ env: GIT_HTTP_LOW_SPEED_LIMIT: 1000 GIT_HTTP_LOW_SPEED_TIME: 60 HUSKY: 0 - SETUP_NODE_TIMEOUT_MINUTES: 10 on: workflow_call: @@ -47,10 +46,6 @@ on: required: false default: "" type: string - timeout-minutes: - required: false - default: 90 - type: number permissions: contents: read @@ -63,7 +58,6 @@ jobs: cache-relay: if: inputs.runtime-source == 'checkout' runs-on: ubuntu-slim - timeout-minutes: 15 permissions: actions: read contents: read @@ -71,7 +65,7 @@ jobs: cache-hit: ${{ steps.cache.outputs.cache-hit }} steps: - uses: actions/checkout@v7 - timeout-minutes: 4 + timeout-minutes: 60 with: persist-credentials: false @@ -144,7 +138,6 @@ jobs: (inputs.runtime-source == 'published' || (needs.cache-relay.result == 'success' && needs.cache-relay.outputs.cache-hit != 'true')) runs-on: ${{ inputs.runtime-source == 'checkout' && inputs.os || 'ubuntu-latest' }} - timeout-minutes: ${{ inputs.timeout-minutes }} permissions: actions: read contents: read @@ -152,7 +145,7 @@ jobs: packages: read steps: - uses: actions/checkout@v7 - timeout-minutes: 4 + timeout-minutes: 60 with: persist-credentials: false @@ -197,7 +190,7 @@ jobs: - if: inputs.runtime-source == 'checkout' && steps.cache.outputs.cache-hit != 'true' uses: ./.github/actions/setup-node - timeout-minutes: ${{ fromJSON(env.SETUP_NODE_TIMEOUT_MINUTES) }} + timeout-minutes: 60 - name: Capture verified addon source identity if: inputs.runtime-source == 'checkout' && steps.cache.outputs.cache-hit != 'true' diff --git a/.github/workflows/sdk-rust.yml b/.github/workflows/sdk-rust.yml index abe13e28bb..902ae8ed11 100644 --- a/.github/workflows/sdk-rust.yml +++ b/.github/workflows/sdk-rust.yml @@ -7,7 +7,6 @@ env: HUSKY: 0 POWERSHELL_UPDATECHECK: Off SDK_HOME: ${{ inputs.sdk-home }} - SETUP_NODE_TIMEOUT_MINUTES: 10 on: workflow_call: @@ -27,11 +26,10 @@ jobs: rust: name: "Rust (${{ matrix.os }})" if: contains(fromJSON('["all","linux-x64","darwin-arm64","win32-x64"]'), inputs.platform) - timeout-minutes: 60 strategy: fail-fast: false matrix: - os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-latest"]' || inputs.platform == 'darwin-arm64' && '["macos-26-xlarge"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-26-xlarge","windows-latest"]') }} + os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-latest"]' || inputs.platform == 'darwin-arm64' && '["macos-26-xlarge-agent-runtime"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-26-xlarge-agent-runtime","windows-latest"]') }} runs-on: ${{ matrix.os }} defaults: run: @@ -40,9 +38,11 @@ jobs: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: 1 + # Preserve source-line backtraces without full variable/type debug information. + CARGO_PROFILE_DEV_DEBUG: line-tables-only + CARGO_PROFILE_TEST_DEBUG: line-tables-only steps: - uses: actions/checkout@v7 - timeout-minutes: 4 with: persist-credentials: false - uses: ./.github/actions/download-sdk-runtime @@ -128,10 +128,8 @@ jobs: name: "Rust (Alpine x64)" if: inputs.platform == 'all' || inputs.platform == 'linuxmusl-x64' runs-on: ubuntu-latest - timeout-minutes: 60 steps: - uses: actions/checkout@v7 - timeout-minutes: 4 with: persist-credentials: false - uses: ./.github/actions/download-sdk-runtime diff --git a/.github/workflows/sdk.yml b/.github/workflows/sdk.yml index 56c165db89..852163ec93 100644 --- a/.github/workflows/sdk.yml +++ b/.github/workflows/sdk.yml @@ -9,7 +9,6 @@ env: COPILOT_BAZEL_ANG_PR_SAMPLED_MODE: ${{ vars.COPILOT_BAZEL_ANG_PR_SAMPLED_MODE }} HUSKY: 0 POWERSHELL_UPDATECHECK: Off - SETUP_NODE_TIMEOUT_MINUTES: 10 on: pull_request: @@ -42,7 +41,6 @@ jobs: id: checkout if: github.event_name == 'pull_request' continue-on-error: true - timeout-minutes: 4 with: # Include the synthetic merge commit's base parent without fetching # repository history. @@ -177,9 +175,14 @@ jobs: sdk-home: ${{ steps.detect.outputs.sdk-home }} steps: - uses: actions/checkout@v7 - timeout-minutes: 4 with: persist-credentials: false + sparse-checkout: | + /src/sdk/package.json + /script/sea-build.ts + /package.json + /nodejs/package.json + sparse-checkout-cone-mode: false - id: detect run: | if [[ -f src/sdk/package.json && -f script/sea-build.ts ]]; then @@ -202,7 +205,6 @@ jobs: needs: detect-layout if: github.event_name != 'merge_group' && needs.detect-layout.outputs.runtime-source == 'checkout' runs-on: ubuntu-latest-xl - timeout-minutes: 60 concurrency: group: windows-x64-addons-release-windows-e2e-no-icf-${{ github.sha }} queue: max @@ -212,7 +214,6 @@ jobs: packages: read steps: - uses: actions/checkout@v7 - timeout-minutes: 4 with: persist-credentials: false @@ -242,7 +243,7 @@ jobs: - uses: ./.github/actions/setup-node if: steps.cache.outputs.cache-hit != 'true' - timeout-minutes: ${{ fromJSON(env.SETUP_NODE_TIMEOUT_MINUTES) }} + timeout-minutes: 60 - uses: ./.github/actions/setup-bazel if: steps.cache.outputs.cache-hit != 'true' with: @@ -352,7 +353,6 @@ jobs: artifact: Linux-X64-gnu bazel-config: e2e-no-lto shared-cli-build-compatible: true - timeout-minutes: 60 secrets: inherit build-runtime-linuxmusl-x64: @@ -372,7 +372,6 @@ jobs: target: linuxmusl-x64 artifact: Linux-X64-musl bazel-config: e2e-no-lto - timeout-minutes: 60 secrets: inherit build-runtime-linuxmusl-arm64: @@ -392,7 +391,6 @@ jobs: target: linuxmusl-arm64 artifact: Linux-ARM64-musl bazel-config: e2e-no-lto - timeout-minutes: 60 secrets: inherit build-runtime-darwin-arm64: @@ -408,13 +406,12 @@ jobs: with: runtime-source: ${{ needs.detect-layout.outputs.runtime-source }} sdk-home: ${{ needs.detect-layout.outputs.sdk-home }} - os: macos-26-xlarge + os: macos-26-xlarge-agent-runtime target: darwin-arm64 artifact: macOS-ARM64 bazel-config: e2e-no-lto use-bazel-addons-on-darwin: true shared-cli-build-compatible: true - timeout-minutes: 60 secrets: inherit build-runtime-win32-x64: @@ -436,7 +433,6 @@ jobs: bazel-config: windows-e2e-no-icf shared-cli-build-compatible: true prebuilt-addons-artifact: ${{ needs.detect-layout.outputs.runtime-source == 'checkout' && 'sdk-bazel-cross-addons-win32-x64-release' || '' }} - timeout-minutes: 60 secrets: inherit sdk-linux-x64: @@ -528,13 +524,34 @@ jobs: id-token: write steps: - uses: actions/checkout@v7 - timeout-minutes: 10 with: persist-credentials: false - fetch-depth: 0 + fetch-depth: ${{ github.event_name == 'pull_request' && 2 || 1 }} + - name: Fetch generation comparison baseline + if: needs.detect-layout.outputs.runtime-source == 'checkout' && (github.event_name == 'merge_group' || github.event_name == 'push') + env: + BASELINE_SHA: ${{ github.event.merge_group.base_sha || github.event.before }} + CHECKOUT_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + if [[ "$GITHUB_EVENT_NAME" == "push" && "$BASELINE_SHA" =~ ^0+$ ]]; then + echo "New branch push has no generation baseline; checking all projections." + exit 0 + fi + if [[ ! "$BASELINE_SHA" =~ ^[0-9a-fA-F]{40}$ ]]; then + echo "::error::Missing or invalid SDK generation baseline SHA." + exit 1 + fi + if git cat-file -e "$BASELINE_SHA^{commit}" 2>/dev/null; then + exit 0 + fi + auth_header="$(printf 'x-access-token:%s' "$CHECKOUT_TOKEN" | base64 | tr -d '\n')" + GIT_CONFIG_COUNT=1 \ + GIT_CONFIG_KEY_0=http.https://github.com/.extraheader \ + GIT_CONFIG_VALUE_0="AUTHORIZATION: basic $auth_header" \ + git fetch --no-tags --depth=1 origin "$BASELINE_SHA" - if: needs.detect-layout.outputs.runtime-source == 'checkout' uses: ./.github/actions/setup-node - timeout-minutes: ${{ fromJSON(env.SETUP_NODE_TIMEOUT_MINUTES) }} with: install-deps: "false" - if: needs.detect-layout.outputs.runtime-source == 'checkout' diff --git a/BUILD.bazel b/BUILD.bazel index 4fa953fbf5..b9465c7090 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1,3 +1,8 @@ +exports_files( + ["test/harness/sdk-diagnostics.mjs"], + visibility = ["//src/runtime-bin:__pkg__"], +) + SHARED_CODEGEN_INPUTS = [ "nodejs/scripts/releaseArtifacts.ts", "nodejs/src/cliVersion.ts", diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 74519107b4..6513793b66 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -356,11 +356,11 @@ For TypeScript SDK E2Es, use the existing `nodejs/test/e2e/harness/sdkTestContext.ts` fixture. In the runtime repository, also follow the `e2e-test-author` skill's SDK section. -All six SDKs run full subprocess coverage followed by a short in-process -smoke step on the same runner on Linux/glibc x64, macOS ARM64, Windows x64, -and Linux/musl x64. Smoke still runs if the subprocess tests fail, and either -failure fails the job. Java uses JDK 25 on all four platforms, plus a -Linux/glibc JDK 17 compatibility job using precompiled classes. +All SDKs run full subprocess coverage and in-process smoke on Linux/glibc +x64, macOS ARM64, Windows x64, and Linux/musl x64. Windows .NET uses complementary +jobs per framework. Smoke also runs after subprocess failures; either failure +fails the job. Java uses JDK 25 on these platforms, +plus a Linux/glibc JDK 17 compatibility job using precompiled classes. Merge groups retain the reduced Linux TypeScript CAPI subprocess coverage. The `sdk-typescript` required rollup checks only the Linux CAPI job, including diff --git a/docs/features/custom-agents.md b/docs/features/custom-agents.md index 9e2f597688..965582a01b 100644 --- a/docs/features/custom-agents.md +++ b/docs/features/custom-agents.md @@ -25,6 +25,8 @@ flowchart TD ## Defining custom agents +To discover Markdown definitions from versioned packages or vendored folders, use `agentDirectories` or [repository customization locations](./customization-locations.md). + Pass `customAgents` when creating a session. Each agent needs at minimum a `name` and `prompt`.
diff --git a/docs/features/customization-locations.md b/docs/features/customization-locations.md new file mode 100644 index 0000000000..703c5d5066 --- /dev/null +++ b/docs/features/customization-locations.md @@ -0,0 +1,55 @@ + + +# Customization discovery locations + +GitHub Copilot can discover versioned skills, agents, instructions, and file hooks directly from package installations, submodules, and vendored directories. You don't need to copy them into conventional directories. + +## Repository settings + +Declare checkout-relative directories in `.github/copilot/settings.json`: + +```json +{ + "skillDirectories": ["node_modules/@example/team/skills"], + "agentDirectories": ["vendor/team/agents"], + "instructionDirectories": ["vendor/team/instructions"], + "hookDirectories": ["vendor/team/hooks"] +} +``` + +Each list adds discovery locations for its own customization type. Conventional locations continue to work. A key in `.github/copilot/settings.local.json` replaces only that repository list; an empty list removes its repository contribution. Omitted keys inherit the checked-in list. Repository lists don't replace SDK inputs or existing user skill directories. + +Repository paths must be nonempty, literal directories within the declaring checkout. Absolute paths, home or environment-variable expansion, globs, and escapes outside the checkout are rejected. Links and junctions are allowed only when their resolved roots and discovered descendants remain inside the checkout. A missing directory is reported and can be discovered after installation and an explicit customization reload. Copilot doesn't install packages or watch these directories. + +File content from repository-defined discovery locations is read through a validated, opened file handle. Retargeting a link outside the checkout between discovery and invocation doesn't authorize reading its new target. + +Repository locations require trust covering the declaring checkout root and remain subject to each customization type's existing discovery switches, disabled entries, and managed policy. Persisted trust for only a subdirectory doesn't authorize its parent checkout's settings. Listing a location doesn't enable a disabled customization type or grant filesystem access. + +In the CLI, remembered folder trust refreshes repository customizations in the current session, even if model catalog initialization failed. `/skills reload` also rechecks persisted trust and repository discovery settings. + +## SDK inputs + +Session creation, resume, and option updates accept the same four lists. TypeScript and Java use `skillDirectories`, `agentDirectories`, `instructionDirectories`, and `hookDirectories`. Python and Rust use snake case; Go and .NET use Pascal case. + +SDK directories can be outside the checkout. Leading and trailing whitespace is trimmed, and blank entries are ignored. Relative SDK paths resolve once against the request's effective working directory when supplied. Changing the working directory doesn't move retained SDK roots. Supplying a new relative list uses the new effective working directory. An empty list clears only the explicit SDK contribution; omission or `null` leaves it unchanged during an update. + +If adopting a `hookDirectories` replacement fails or is canceled before the new hook service takes ownership, the previous directory option and live hook service remain in place. Overlapping failed updates restore the retained value rather than another failed update's directories. A failed update doesn't roll back a newer accepted update. + +Explicit agent and file-hook directories don't require ambient configuration discovery. `enableFileHooks` controls repository-origin hooks, including conventional `.github/hooks`, but not explicit SDK hook directories or callback `hooks`. Managed restrictions and hook disables still apply. + +Directory inputs are discovery configuration, not general filesystem grants. They don't widen `additionalDirectories`. Existing file-hook sandbox rules remain in effect. + +Loading active local skills adds read-only grants for their contributing SDK skill directories under the runtime's skill-resource policy. Empty, ignored, and disabled skill sources don't add those grants. Repository-defined skill directories don't add them. + +## File formats and precedence + +| Type | Discovered content | +| --- | --- | +| Skills | A direct `SKILL.md` bundle or recursively discovered topmost bundles | +| Agents | Existing Markdown agent definitions | +| Repository instructions | Recursive `*.instructions.md` files, with existing frontmatter and `applyTo` matching | +| Hooks | Existing hook JSON configurations | + +Repository instruction directories don't import package `AGENTS.md` hierarchies or `.claude/rules`. Existing SDK instruction-directory formats remain supported. Each subsystem preserves its existing source precedence. Overlapping hook file references are deduplicated; distinct authored actions aren't collapsed because their commands happen to match. + +File-backed command hooks receive `COPILOT_HOOK_DIR`, the directory containing their configuration file. Use it to locate adjacent scripts and resources. Hook working-directory semantics don't change: they remain anchored to the consuming workspace. `COPILOT_HOOK_DIR` is an execution environment variable, not JSON template expansion; `COPILOT_PLUGIN_ROOT` retains its separate plugin-root behavior. Inline hooks and SDK callbacks don't gain a fabricated configuration directory. diff --git a/docs/features/hooks.md b/docs/features/hooks.md index a3cfc0f503..b0cd89d6e9 100644 --- a/docs/features/hooks.md +++ b/docs/features/hooks.md @@ -35,6 +35,55 @@ All hooks are **optional**—register only the ones you need. Returning `null` ( ## Registering hooks +Callback `hooks` remain separate from file-backed `hookDirectories`. For packaged hook configurations and adjacent resources, see [customization discovery locations](./customization-locations.md). + +### Agent-scoped declarative hooks + +Custom agents can declare a `hooks` event map in `.agent.md` frontmatter or in +the SDK's custom-agent configuration. These are declarative command or HTTP +actions, not SDK callback functions. They run only while that agent is selected, +in addition to applicable session, user, repository, plugin, and managed-policy +hooks. Changing or clearing the selected agent changes the scoped hooks for the +next invocation without changing other sessions or concurrent subagents. + +```yaml +--- +name: formatter +description: Formats changed files +hooks: + PostToolUse: + - type: command + command: ./scripts/format-changed-files.sh + windows: ./scripts/format-changed-files.ps1 +--- +Format the files you change. +``` + +Both runtime event names such as `postToolUse` and supported VS Code aliases +such as `PostToolUse` are accepted. `command` supplies the fallback shell command; +`windows`, `linux`, and `osx` override it on the corresponding platform. Hook +commands are relative to the executing session's repository root, unless they +set an explicit working directory. Hook payloads and decisions follow the +runtime's existing hook contracts. + +When a local agent runs as a subagent, its scoped `Stop` (`agentStop`) actions +run at `SubagentStop` instead of ordinary turn stop. An explicit `SubagentStop` +action on that agent still observes its own children. Stop callbacks receive +`stop_hook_active: true` after a prior block. After eight consecutive scoped +Stop continuations, the runtime ignores further scoped blocks and accepts the +child's final response unless a parent lifecycle hook still blocks completion. + +Repository and inherited-agent hooks require workspace admission. Agent +discovery or selection does not itself grant trust. Existing hook-disable +settings, enterprise restrictions, sandbox permissions, and timeouts also apply +to scoped actions. SDK-provided agents cannot claim managed-plugin privileges. +Remote or organization-hosted agent definitions do not execute scoped hooks, +and scoped Stop dispatch is supported for local runtime subagents, not external +AHP participants. + +Unsupported event names are reported before event selection. Action preparation +errors affect only their declared event; shared and managed-policy hooks still run. + Pass a `hooks` object when you create (or resume) a session. Every example below follows this pattern.
diff --git a/docs/features/mcp.md b/docs/features/mcp.md index 4be11f2452..1c93c82710 100644 --- a/docs/features/mcp.md +++ b/docs/features/mcp.md @@ -57,7 +57,7 @@ Inventory retries concurrent graph replacement a bounded number of times and returns an error if it cannot obtain a consistent snapshot. It never omits `live` to hide a graph race. -An explicit `startServer` or `restartServer` resolves and initializes only the +A name-only `startServer` or `restartServer` resolves and initializes only the requested server after a cold session resume without first calling `list`. This includes user and plugin configurations and the built-in GitHub server configured through `githubMcpToolConfig`. It does not start other configured diff --git a/docs/features/plugin-directories.md b/docs/features/plugin-directories.md index 456ef88e7f..393fc4c048 100644 --- a/docs/features/plugin-directories.md +++ b/docs/features/plugin-directories.md @@ -422,6 +422,63 @@ for (const plugin of plugins.plugins) { Plugins loaded via `--plugin-dir` appear in this list with their cache path set to the directory you provided. Marketplace installs are tagged with their registry source. +## Retrying plugins required by managed settings + +When managed settings require plugins through `enabledPlugins`, the runtime installs or updates them before chat messages. If that fails, the session emits a `session.warning` with `warningType: "managed_plugins"` and waits an hour, or until the managed settings change, before trying again. The experimental `session.rpc.plugins.retryManaged()` tries again right away, for example from a Retry action next to that warning. + +Call it with no arguments to retry every plugin the managed settings require, or pass `plugin@marketplace` specs in `plugins` to retry only those. The result has one entry per plugin with a `status`: `installed`, `updated`, `already_present`, `failed`, `deferred`, or `not_required`. `failed` and `deferred` entries include an `error`. The session also emits the usual `session.info` progress and `session.warning` failure events. + +
+Node.js / TypeScript + + +```typescript +import { CopilotClient } from "@github/copilot-sdk"; + +async function main() { + const client = new CopilotClient(); + await client.start(); + const session = await client.createSession({ + onPermissionRequest: async () => ({ kind: "approve-once" }), + }); + + // Retry every plugin the managed settings require, for example from a Retry action. + const result = await session.rpc.plugins.retryManaged(); + for (const entry of result.plugins) { + console.log(`${entry.spec}: ${entry.status}${entry.error ? ` (${entry.error})` : ""}`); + } + + // Or retry only the required plugins that are still missing. + const { plugins } = await session.rpc.plugins.list(); + const missing = plugins + .filter((plugin) => plugin.managedDesiredEnabled === true && plugin.installed === false) + .map((plugin) => `${plugin.name}@${plugin.marketplace}`); + await session.rpc.plugins.retryManaged({ plugins: missing }); +} + +main(); +``` + + +```typescript +// Retry every plugin the managed settings require, for example from a Retry action. +const result = await session.rpc.plugins.retryManaged(); +for (const entry of result.plugins) { + console.log(`${entry.spec}: ${entry.status}${entry.error ? ` (${entry.error})` : ""}`); +} + +// Or retry only the required plugins that are still missing. +const { plugins } = await session.rpc.plugins.list(); +const missing = plugins + .filter((plugin) => plugin.managedDesiredEnabled === true && plugin.installed === false) + .map((plugin) => `${plugin.name}@${plugin.marketplace}`); +await session.rpc.plugins.retryManaged({ plugins: missing }); +``` + +
+ +Runtimes that do not prepare managed plugins for the session reject the call. + ## Troubleshooting * **"no plugin.json or SKILL.md found in <dir>"** — the directory exists but doesn't qualify as a plugin. Add a `plugin.json` manifest at the root (or under `.github/`), or include a top-level `SKILL.md`. diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index 7fcfbfef99..77df3cba7f 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -403,29 +403,7 @@ async Task StartCoreAsync(CancellationToken ct) ffiEnvironment["COPILOT_DISABLE_KEYTAR"] = "1"; } - var ffiArgs = new List(); - if (_options.LogLevel is { } logLevel && !string.IsNullOrEmpty(logLevel.Value)) - { - ffiArgs.AddRange(["--log-level", logLevel.Value]); - } - if (!string.IsNullOrEmpty(_options.GitHubToken)) - { - ffiArgs.AddRange(["--auth-token-env", "COPILOT_SDK_AUTH_TOKEN"]); - } - var useLoggedInUser = _options.UseLoggedInUser ?? string.IsNullOrEmpty(_options.GitHubToken); - if (!useLoggedInUser) - { - ffiArgs.Add("--no-auto-login"); - } - if (_options.SessionIdleTimeoutSeconds is > 0) - { - ffiArgs.AddRange(["--session-idle-timeout", _options.SessionIdleTimeoutSeconds.Value.ToString(CultureInfo.InvariantCulture)]); - } - if (_options.EnableRemoteSessions) - { - ffiArgs.Add("--remote"); - } - + var ffiArgs = BuildInProcessRuntimeArguments(_options); var explicitCliPath = System.Environment.GetEnvironmentVariable("COPILOT_CLI_PATH"); if (string.IsNullOrEmpty(explicitCliPath)) { @@ -1284,6 +1262,7 @@ public async Task CreateSessionAsync(SessionConfig config, Cance var request = new CreateSessionRequest( config.Model, localSessionId, + config.Name, config.ClientName, config.ReasoningEffort, config.ReasoningSummary, @@ -1343,10 +1322,13 @@ public async Task CreateSessionAsync(SessionConfig config, Cance Cloud: config.Cloud, RefreshCustomInstructions: config.RefreshCustomInstructions, InstructionDirectories: config.InstructionDirectories, + AgentDirectories: config.AgentDirectories, + HookDirectories: config.HookDirectories, PluginDirectories: config.PluginDirectories, DisabledMcpServers: config.DisabledMcpServers, LargeOutput: config.LargeOutput, ToolSearch: config.ToolSearch, + ImageGeneration: config.ImageGeneration, Memory: config.Memory, Canvases: config.Canvases, RequestCanvasRenderer: config.RequestCanvasRenderer, @@ -1615,10 +1597,13 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes RemoteSession: config.RemoteSession, ContinuePendingWork: config.ContinuePendingWork, InstructionDirectories: config.InstructionDirectories, + AgentDirectories: config.AgentDirectories, + HookDirectories: config.HookDirectories, PluginDirectories: config.PluginDirectories, DisabledMcpServers: config.DisabledMcpServers, LargeOutput: config.LargeOutput, ToolSearch: config.ToolSearch, + ImageGeneration: config.ImageGeneration, Memory: config.Memory, Canvases: config.Canvases, RequestCanvasRenderer: config.RequestCanvasRenderer, @@ -2579,6 +2564,40 @@ private static void ApplyTelemetryEnvironment(IDictionary envir return GetBundledNativePath(OperatingSystem.IsWindows() ? "copilot.exe" : "copilot", out searchedPath); } + private static List BuildInProcessRuntimeArguments(CopilotClientOptions options) + { + var args = new List(); + if (options.LogLevel is { } logLevel && !string.IsNullOrEmpty(logLevel.Value)) + { + args.AddRange(["--log-level", logLevel.Value]); + } + if (!string.IsNullOrEmpty(options.GitHubToken)) + { + args.AddRange(["--auth-token-env", "COPILOT_SDK_AUTH_TOKEN"]); + } + var useLoggedInUser = options.UseLoggedInUser ?? string.IsNullOrEmpty(options.GitHubToken); + if (!useLoggedInUser) + { + args.Add("--no-auto-login"); + } + if (options.SessionIdleTimeoutSeconds is > 0) + { + args.AddRange(["--session-idle-timeout", options.SessionIdleTimeoutSeconds.Value.ToString(CultureInfo.InvariantCulture)]); + } + if (options.EnableRemoteSessions) + { + args.Add("--remote"); + } + + var cliLoginSyncIntegrationId = + System.Environment.GetEnvironmentVariable("GITHUB_COPILOT_INTEGRATION_ID")?.Trim(); + if (!string.IsNullOrEmpty(cliLoginSyncIntegrationId)) + { + args.Add($"--cli-login-sync-integration-id={cliLoginSyncIntegrationId}"); + } + return args; + } + private static string? GetBundledNativePath(string binaryName, out string searchedPath) { // Always use portable RID (e.g., linux-x64) to match the build-time placement, @@ -3210,6 +3229,7 @@ public static string Escape(string arg) internal record CreateSessionRequest( string? Model, string? SessionId, + string? Name, string? ClientName, string? ReasoningEffort, ReasoningSummary? ReasoningSummary, @@ -3269,10 +3289,13 @@ internal record CreateSessionRequest( CloudSessionOptions? Cloud = null, bool? RefreshCustomInstructions = null, IList? InstructionDirectories = null, + IList? AgentDirectories = null, + IList? HookDirectories = null, IList? PluginDirectories = null, [property: JsonPropertyName("disabledMcpServers")] IList? DisabledMcpServers = null, LargeToolOutputConfig? LargeOutput = null, ToolSearchConfig? ToolSearch = null, + ImageGenerationConfig? ImageGeneration = null, MemoryConfiguration? Memory = null, #pragma warning disable GHCP001 IList? Canvases = null, @@ -3391,10 +3414,13 @@ internal record ResumeSessionRequest( RemoteSessionMode? RemoteSession = null, bool? ContinuePendingWork = null, IList? InstructionDirectories = null, + IList? AgentDirectories = null, + IList? HookDirectories = null, IList? PluginDirectories = null, [property: JsonPropertyName("disabledMcpServers")] IList? DisabledMcpServers = null, LargeToolOutputConfig? LargeOutput = null, ToolSearchConfig? ToolSearch = null, + ImageGenerationConfig? ImageGeneration = null, MemoryConfiguration? Memory = null, #pragma warning disable GHCP001 IList? Canvases = null, diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs index b21d884f1d..abd757747b 100644 --- a/dotnet/src/Generated/Rpc.cs +++ b/dotnet/src/Generated/Rpc.cs @@ -107,2239 +107,2383 @@ internal sealed class ConnectRequest public string? Token { get; set; } } -/// Hosting capabilities and session capacity advertised by an environment. +/// The user's sandbox settings raised to the managed floor. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class EnvironmentCapabilities +internal sealed class SandboxResolveFlooredConfigResult { - /// Advertised Agent Host Protocol version. - [JsonPropertyName("ahpVersion")] - public string? AhpVersion { get; set; } - - /// Current session count, when advertised. - [JsonPropertyName("currentSessions")] - public long? CurrentSessions { get; set; } + /// The floored sandbox config, normalized through the declared sandbox shape so only declared fields in a fixed order are present. + [JsonPropertyName("sandboxJson")] + public JsonElement SandboxJson { get; set; } +} - /// Feature identifiers advertised by the environment. - [JsonPropertyName("features")] - public IList Features { get => field ??= []; set; } +/// The user's sandbox settings and the managed sandbox floor to raise them to. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SandboxResolveFlooredConfigParams +{ + /// The effective managed `sandbox` slice acting as a floor. Omitted when no administrator policy applies, in which case the user's own settings stand unchanged. + [JsonPropertyName("managedSandbox")] + public JsonElement? ManagedSandbox { get; set; } - /// Maximum session capacity, when advertised. - [JsonPropertyName("maxSessions")] - public long? MaxSessions { get; set; } + /// The user's own `sandbox` settings slice. Omitted when the user has configured none, which still yields the managed floor rather than a disabled sandbox. + [JsonPropertyName("userSandbox")] + public JsonElement? UserSandbox { get; set; } } -/// Safe discovery information. Host-side relay bootstrap credentials are never included. +/// The prepared file-picker policy, or none when the sandbox is disabled. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class GitHubEnvironment +internal sealed class SandboxPreparePickerPolicyResult { - /// Hosting capabilities advertised by the environment. - [JsonPropertyName("capabilities")] - public EnvironmentCapabilities? Capabilities { get; set; } - - /// Identifier assigned by Mission Control. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// The prepared policy to pass back to `sandbox.filterPickerCandidates`. Absent when the sandbox is disabled or absent, in which case filtering keeps every candidate. + [JsonPropertyName("sandboxJson")] + public JsonElement? SandboxJson { get; set; } +} - /// Compute kind reported by Mission Control. - [JsonPropertyName("kind")] - public EnvironmentKind Kind { get; set; } +/// The working directory and sandbox settings to build a file-picker policy from. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SandboxPreparePickerPolicyParams +{ + /// The session's working directory. Git discovery and grant canonicalization are resolved against it. + [JsonPropertyName("cwd")] + public string Cwd { get; set; } = string.Empty; - /// Discovery labels attached to the environment. - [JsonPropertyName("labels")] - public IDictionary? Labels { get; set; } + /// The session's resolved `sandbox` slice. Omitted, or disabled, yields no policy and every candidate is kept. + [JsonPropertyName("sandbox")] + public JsonElement? Sandbox { get; set; } +} - /// Timestamp of the last heartbeat received by Mission Control. - [JsonPropertyName("lastHeartbeatAt")] - public string? LastHeartbeatAt { get; set; } +/// Per-candidate allow decisions, positionally aligned with the request. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SandboxFilterPickerCandidatesResult +{ + /// One decision per candidate, in the same order. Always the same length as the request's `candidates`. + [JsonPropertyName("allowed")] + public IList Allowed { get => field ??= []; set; } +} - /// Human-readable environment name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; +/// One file-picker suggestion offered up for the sandbox filesystem check. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SandboxPickerCandidate +{ + /// Whether the suggestion is a directory. A directory the policy does not itself allow may still be needed as a waypoint to an allowed subtree. + [JsonPropertyName("isDirectory")] + public bool IsDirectory { get; set; } - /// Organization identifier, when the environment belongs to an organization. - [JsonPropertyName("orgId")] - public string? OrgId { get; set; } + /// Absolute path the suggestion would attach. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; +} - /// Identifier of the environment owner. - [JsonPropertyName("ownerId")] - public string? OwnerId { get; set; } +/// A batch of file-picker candidates to check against a prepared policy. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SandboxFilterPickerCandidatesParams +{ + /// The candidates to check, in the order the answers are returned. + [JsonPropertyName("candidates")] + public IList Candidates { get => field ??= []; set; } - /// Owner category reported by Mission Control. - [JsonPropertyName("ownerType")] - public string? OwnerType { get; set; } + /// The session's working directory, matching the one the policy was prepared from. + [JsonPropertyName("cwd")] + public string Cwd { get; set; } = string.Empty; - /// Open-ended operational status vocabulary. - [JsonPropertyName("status")] - public string Status { get; set; } = string.Empty; + /// The policy returned by `sandbox.preparePickerPolicy`. Omitted keeps every candidate. + [JsonPropertyName("sandbox")] + public JsonElement? Sandbox { get; set; } } -/// Environments visible to the authenticated caller and matching the supplied filters. +/// The sandbox a named feature actually launches under. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class EnvironmentsListResult +internal sealed class SandboxEffectiveForOptionalResult { - /// Safe discovery records without relay bootstrap credentials. - [JsonPropertyName("environments")] - public IList Environments { get => field ??= []; set; } + /// The effective sandbox config for the requested feature, or a disabled sandbox when the feature is routed out of it. + [JsonPropertyName("sandboxJson")] + public JsonElement SandboxJson { get; set; } } -/// Optional discovery filters supported by GitHub Mission Control. +/// A sandbox config and the per-feature routing flag to resolve it for. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class EnvironmentsListRequest +internal sealed class SandboxEffectiveForOptionalParams { - /// Restrict discovery to this compute kind. - [JsonPropertyName("kind")] - public EnvironmentKind? Kind { get; set; } + /// The routing flag naming the feature being launched, such as `sandboxMcpServers` or `sandboxLspServers`. + [JsonPropertyName("routingFlag")] + public string RoutingFlag { get; set; } = string.Empty; - /// Operational status, such as online, offline, degraded, waking, or draining. - [JsonPropertyName("status")] - public string? Status { get; set; } + /// The session's resolved `sandbox` slice. Omitted when the host has none, which yields a disabled sandbox rather than an error. + [JsonPropertyName("sandbox")] + public JsonElement? Sandbox { get; set; } } -/// Safe discovery information for the requested environment. +/// The startup warning, absent when the machine can run a sandbox. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class EnvironmentsGetResult +internal sealed class SandboxUnsupportedStartupNoticeResult { - /// The requested environment without relay bootstrap credentials. - [JsonPropertyName("environment")] - public GitHubEnvironment Environment { get => field ??= new(); set; } + /// The user-facing warning, carrying the platform remedy and the recovery the policy in force actually allows. Absent when this machine supports a sandbox, so a host can call unconditionally and print only what it gets. + [JsonPropertyName("notice")] + public string? Notice { get; set; } } -/// Identify a Mission Control environment to retrieve. +/// The managed sandbox policy in force, which selects which remedy the notice offers. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class EnvironmentsGetRequest +internal sealed class SandboxUnsupportedStartupNoticeParams { - /// Identifier assigned by Mission Control. - [JsonPropertyName("environmentId")] - public string EnvironmentId { get; set; } = string.Empty; + /// True when the sandbox stayed on only because the managed policy could not be determined. Distinguishes a policy that positively enforces the sandbox from one that merely failed to resolve, which is the difference between telling the user to contact an administrator and telling them to wait. Defaults to false. + [JsonPropertyName("enabledByUndeterminedPolicy")] + public bool? EnabledByUndeterminedPolicy { get; set; } + + /// The managed `sandbox` slice, when a policy supplies one. The notice treats the sandbox as enforced only when this both enables it and sets `failIfUnavailable`, because a managed sandbox that tolerates being unavailable still leaves the user able to turn it off. + [JsonPropertyName("managedSandbox")] + public JsonElement? ManagedSandbox { get; set; } } -/// Acknowledgement that the requested environment was deleted. +/// One slash command, as typed, to test against the enforcement-block allowlist. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class EnvironmentsDeleteResult +internal sealed class SandboxEnforcementCommandAllowedParams { + /// The command's arguments, in order. Pass an empty array for a bare command: several entries in the allowlist admit the bare form and refuse every argument. + [JsonPropertyName("args")] + public IList Args { get => field ??= []; set; } + + /// The command name as the user typed it, with or without its leading slash. + [JsonPropertyName("commandName")] + public string CommandName { get; set; } = string.Empty; } -/// Identify a user-managed Mission Control environment to delete. +/// The documentation URL a host cites when it declines or downgrades a sandbox setting. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class EnvironmentsDeleteRequest +internal sealed class SandboxDocsUrlResult { - /// User-managed environment to delete. GitHub-managed environments cannot be deleted. - [JsonPropertyName("environmentId")] - public string EnvironmentId { get; set; } = string.Empty; + /// Documentation URL describing the sandbox and the settings that control it. + [JsonPropertyName("url")] + public string Url { get; set; } = string.Empty; } -/// The existing runtime identity and its resource on the listener. +/// Whether this host can run one sandbox policy feature. A session whose effective policy uses an unsupported feature fails each sandboxed command with `reason`, except `filesystem_enumeration`, whose absence degrades sandboxed PowerShell instead of failing it. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HostPublishSessionResult +public sealed class SandboxHostCapability { - /// Canonical runtime ID of the published session. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// The policy feature, as an extensible string: ignore names you do not recognize. Known values: `network` (sandboxed commands can reach the network; on Linux this needs the tooling for Bubblewrap's private network namespace, such as slirp4netns), `network_filtering` (host rules and the sandbox proxy; on Linux this needs the same tooling as `network`; on Windows it needs Process Security Environment 1.1 host-loopback support or MXC's PSEC 1.0-only proxy-loopback compatibility capability, and a policy that uses it must also set `network.allowLocalNetwork`; compatibility applies only to an explicit identity-less runtime proxy, not general host-loopback access, and other policy restrictions still apply), `denied_paths` (native enforcement of `filesystem.deniedPaths`), `shell` (shell commands inside the sandbox), and `filesystem_enumeration` (enumerate-only filesystem grants; on Windows this needs Process Security Environment 1.1 filesystem enumeration support, and without it sandboxed PowerShell still runs but cannot resolve its current location; other platforms always report it). + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// AHP resource URI for the session on this listener. - [JsonPropertyName("sessionUri")] - public string SessionUri { get; set; } = string.Empty; + /// Human-readable reason and remedy when the feature is unsupported, such as a package to install or an OS update. Present only when `supported` is false. + [JsonPropertyName("reason")] + public string? Reason { get; set; } + + /// Whether this host can run the feature. + [JsonPropertyName("supported")] + public bool Supported { get; set; } } -/// Publishes a resident session attached to the listener's owning connection. +/// Whether the host running this runtime can run the command sandbox. The runtime checks `supported` once per process. A capability answer can change while the process runs, for example after the user installs a missing package. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HostPublishSessionRequest +public sealed class SandboxHostSupport { - /// Listener UUID returned by host.start. - [JsonPropertyName("hostId")] - public string HostId { get; set; } = string.Empty; + /// Sandbox policy features whose availability varies between hosts that can run the backend. Empty when `supported` is false, because no feature can run without a backend. Later runtimes can add entries; ignore an entry whose `name` you do not recognize. + [JsonPropertyName("capabilities")] + public IList Capabilities { get => field ??= []; set; } - /// Canonical runtime session ID attached to the listener's owning connection. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Human-readable reason the sandbox cannot run on this host. Present only when `supported` is false. + [JsonPropertyName("reason")] + public string? Reason { get; set; } + + /// Whether a process-containment backend is usable on this host: Seatbelt on macOS, Bubblewrap on Linux, or ProcessContainer on Windows. + [JsonPropertyName("supported")] + public bool Supported { get; set; } } -/// Metadata advertised by the AHP host, not a guarantee that history can be resumed. +/// Credential-injection capability flags applied while the sandbox is enabled. For the same capability independent of sandboxing, and matched to the credential's GitHub host, see `shell.credentials`; the two are additive. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HostSessionSummary +public sealed class SandboxConfigAuth { - /// Optional activity description advertised by the host. - [JsonPropertyName("activity")] - public string? Activity { get; set; } - - /// Session creation time as an ISO 8601 timestamp. - [JsonPropertyName("createdAt")] - public string CreatedAt { get; set; } = string.Empty; - - /// Last modification time as an ISO 8601 timestamp. - [JsonPropertyName("modifiedAt")] - public string ModifiedAt { get; set; } = string.Empty; - - /// Stable AHP resource URI identifying the session on this host. - [JsonPropertyName("resource")] - public string Resource { get; set; } = string.Empty; + /// Whether to authenticate sandboxed gh through the local masking proxy. The child receives a fake GH_TOKEN; its real value is substituted only at github.com, api.github.com and uploads.github.com (github.com because gh repo clone authenticates git through gh auth git-credential). The repository's GitHub account takes precedence over the Copilot login. Default: false (opt-in). + [JsonPropertyName("gh")] + public bool? Gh { get; set; } - /// Unsigned 32-bit AHP session status bitset (0..=4294967295); unknown bits must be preserved. - [JsonPropertyName("status")] - public long Status { get; set; } + /// Whether to authenticate sandboxed HTTPS git through the local masking proxy. The child receives a fake `http.<url>.extraheader`; the real Authorization header is substituted only at its original HTTPS host, port, and repository path scope. github.com uses the Copilot token; other forges use credentials resolved from the user's own helper on the host. Default: false (opt-in). + [JsonPropertyName("git")] + public bool? Git { get; set; } +} - /// Authoritative title advertised by the host. - [JsonPropertyName("title")] - public string Title { get; set; } = string.Empty; +/// Destinations authorized to receive one masked environment credential. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SandboxMaskedEnvVar +{ + /// Nonempty list of HTTPS injection hostnames or *.example.com patterns. Bare * is not accepted. These grants never override the sandbox network policy. Values in plaintext HTTP requests, URLs, bodies, encoded credentials, and signed requests are not substituted. + [JsonPropertyName("injectHosts")] + public IList InjectHosts { get => field ??= []; set; } } -/// The complete live and dormant advertised catalog, without subscribing to sessions. +/// Whole-value environment credential masking for sandboxed children. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HostListSessionsResult +public sealed class SandboxCredentialsConfig { - /// Complete advertised session summaries, in the host's catalog order. - [JsonPropertyName("sessions")] - public IList Sessions { get => field ??= []; set; } + /// Environment variable names and their HTTPS injection destinations. Absent variables stay absent. No real values or sentinels are stored in this map. + [JsonPropertyName("envVars")] + public IDictionary EnvVars { get => field ??= new Dictionary(); set; } } -/// Reads a running host's catalog or the runtime's saved default catalog. +/// macOS seatbelt experimental options. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HostListSessionsRequest +public sealed class SandboxConfigUserPolicyExperimentalSeatbelt { - /// - /// Listener UUID returned by host.start; the caller must own it. Omit to read - /// the saved default compute-identity catalog without starting a listener. - /// - [JsonPropertyName("hostId")] - public string? HostId { get; set; } + /// Whether the macOS seatbelt profile may access the keychain. + [JsonPropertyName("keychainAccess")] + public bool? KeychainAccess { get; set; } } -/// The resident session the application has materialized on its own connection. +/// Platform-specific experimental policy fields. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HostCreateSessionResult +public sealed class SandboxConfigUserPolicyExperimental { - /// Runtime session UUID materialized on the application's SDK connection. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// macOS seatbelt experimental options. + [JsonPropertyName("seatbelt")] + public SandboxConfigUserPolicyExperimentalSeatbelt? Seatbelt { get; set; } } -/// One application-owned session handoff, requested by the supervised hosting participant. +/// Filesystem rules to merge into the base policy. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HostCreateSessionRequest +public sealed class SandboxConfigUserPolicyFilesystem { - /// Host-selected SDK creation or resume settings, without executable callbacks or tools. - [JsonPropertyName("config")] - public IDictionary Config { get => field ??= new Dictionary(); set; } + /// Whether to clear the policy when the session exits. + [JsonPropertyName("clearPolicyOnExit")] + public bool? ClearPolicyOnExit { get; set; } - /// Unique identity for this participation, independent of the session lifetime. - [JsonPropertyName("handoffId")] - public string HandoffId { get; set; } = string.Empty; + /// Paths explicitly denied. + [JsonPropertyName("deniedPaths")] + public IList? DeniedPaths { get; set; } - /// - /// For an explicit publication, borrow an existing owning-connection resident - /// instead of invoking the resume factory. Cold sessions still use the factory. - /// - [JsonPropertyName("preferResident")] - public bool? PreferResident { get; set; } + /// Paths granted read-only access. + [JsonPropertyName("readonlyPaths")] + public IList? ReadonlyPaths { get; set; } - /// Resume an app-owned durable session instead of creating a new session. - [JsonPropertyName("resume")] - public bool? Resume { get; set; } + /// Paths granted read/write access. + [JsonPropertyName("readwritePaths")] + public IList? ReadwritePaths { get; set; } } -/// Empty acknowledgement for a completed host lifecycle operation. +/// HTTP proxy configuration for sandboxed traffic. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HostReleaseSessionResult +public sealed class SandboxConfigUserPolicyNetworkProxy { -} + /// Optional password for proxy authentication, combined with the URL at spawn time. The persisted value may be a literal password, a `${secret:…}` reference resolved from the OS keychain, or a `${VAR}`/`$VAR` environment reference; it is resolved just before the sandboxed process routes through the proxy. The /sandbox dialog stores a real password in the OS keychain and persists only a `${secret:…}` placeholder (never plaintext in settings.json); the field is masked in the dialog and redacted by /settings show. + [JsonPropertyName("password")] + public string? Password { get; set; } -/// Ends one participation, not the application's session lifetime. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HostReleaseSessionRequest -{ - /// Identity of the participation to release without destroying the session. - [JsonPropertyName("handoffId")] - public string HandoffId { get; set; } = string.Empty; + /// Proxy URL (e.g. http://proxy.example.com:8080). The port is optional and defaults to the scheme's standard port when omitted; an explicit port must be between 1 and 65535. Credentials must not be embedded here — a `user:pass@` authority is rejected; put them in the separate `username`/`password` fields. A credential-free http:// loopback proxy URL is routed through the localhost proxy automatically; loopback covers localhost and any *.localhost subdomain, the whole 127.0.0.0/8 range, ::1, and IPv4-mapped loopback (::ffff:127.0.0.1). An https:// URL, or one with a username/password set, is used as-is. + [JsonPropertyName("url")] + public string Url { get; set; } = string.Empty; + + /// Optional username for proxy authentication. Combined with the URL (and `password`) into `user:pass@host` when the sandboxed process routes through the proxy. + [JsonPropertyName("username")] + public string? Username { get; set; } } -/// Listener readiness, returned only after binding and the supervised participant's SDK handshake. +/// Network rules to merge into the base policy. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HostStartResult +public sealed class SandboxConfigUserPolicyNetwork { - /// GitHub Mission Control environment ID, present when its relay transport is ready. - [JsonPropertyName("environmentId")] - public string? EnvironmentId { get; set; } + /// Hosts allowed through the built-in sandbox proxy. A non-empty list denies unmatched hosts; an absent or empty list allows all hosts not blocked. Supports exact hostnames, IP addresses, and *.example.com for strict subdomains. Host rules do not override the outbound or local-network toggles. + [JsonPropertyName("allowedHosts")] + public IList? AllowedHosts { get; set; } - /// Caller-generated listener UUID. - [JsonPropertyName("hostId")] - public string HostId { get; set; } = string.Empty; + /// Whether traffic to local/loopback addresses is allowed. + [JsonPropertyName("allowLocalNetwork")] + public bool? AllowLocalNetwork { get; set; } - /// Separate host process ID, when provided by a legacy runtime. Absent for in-process listeners. - [JsonPropertyName("pid")] - public long? Pid { get; set; } + /// Whether outbound network traffic is allowed at all. + [JsonPropertyName("allowOutbound")] + public bool? AllowOutbound { get; set; } - /// Secret connection token, absent when authentication is disabled. - [JsonPropertyName("token")] - public string? Token { get; set; } + /// Hosts denied by the built-in sandbox proxy. Deny rules take precedence over allowedHosts. A domain also denies all its subdomains. IP addresses match exactly; *.example.com matches strict subdomains, and * denies every host. + [JsonPropertyName("blockedHosts")] + public IList? BlockedHosts { get; set; } - /// Actual bound WebSocket URL, including the allocated port. - [JsonPropertyName("url")] - public string? Url { get; set; } + /// HTTP(S) proxy for sandboxed traffic. This is the built-in local proxy's upstream: every sandboxed command reaches it through a loopback listener, so credentials stay in the runtime and never reach the child. On Windows the sandbox also needs local network access, because it reaches that listener over host loopback. Configure credentials in the separate username/password fields. The transient local listener URL is never persisted. + [JsonPropertyName("proxy")] + public SandboxConfigUserPolicyNetworkProxy? Proxy { get; set; } } -/// GitHub Mission Control registration options. The compute ID is application-owned and stable. +/// macOS seatbelt-specific options. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HostGitHubEnvironmentOptions +public sealed class SandboxConfigUserPolicySeatbelt { - /// Stable application installation identity, reused across host restarts. - [JsonPropertyName("computeId")] - public string ComputeId { get; set; } = string.Empty; - - /// Human-readable environment display name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - /// Require sealed, connection-bound authentication (default true), independent of listener address. False permits unsealed direct credentials and unbound sealed relay credentials; use only when the caller fully controls and trusts the direct transport. Relay token encryption remains mandatory. - [JsonPropertyName("requireConnectionBinding")] - public bool? RequireConnectionBinding { get; set; } + /// Whether the macOS seatbelt profile may access the keychain. + [JsonPropertyName("keychainAccess")] + public bool? KeychainAccess { get; set; } } -/// Local WebSocket transport options. +/// User-managed sandbox policy fragment merged into the auto-discovered base policy. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HostLocalServerOptions +public sealed class SandboxConfigUserPolicy { - /// Listener hostname. Defaults to 127.0.0.1; explicit non-loopback binds are allowed. - [JsonPropertyName("hostname")] - public string? Hostname { get; set; } + /// Deprecated legacy location for `seatbelt`; read only when the top-level `seatbelt` is absent. + [JsonPropertyName("experimental")] + public SandboxConfigUserPolicyExperimental? Experimental { get; set; } - /// Listener port. Omitted or zero requests an OS-allocated port. - [JsonPropertyName("port")] - public int? Port { get; set; } + /// Filesystem rules to merge into the base policy. + [JsonPropertyName("filesystem")] + public SandboxConfigUserPolicyFilesystem? Filesystem { get; set; } - /// Require token authentication (default true). Cannot be false with a token. - [JsonPropertyName("requireConnectionToken")] - public bool? RequireConnectionToken { get; set; } + /// Network rules to merge into the base policy. + [JsonPropertyName("network")] + public SandboxConfigUserPolicyNetwork? Network { get; set; } - /// Nonempty connection token. Generated randomly when required and omitted. - [JsonPropertyName("token")] - public string? Token { get; set; } + /// macOS seatbelt options to merge into the base policy. + [JsonPropertyName("seatbelt")] + public SandboxConfigUserPolicySeatbelt? Seatbelt { get; set; } } -/// Starts a supervised AHP host with at least one explicitly selected transport. +/// Resolved sandbox configuration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HostStartRequest +public sealed class SandboxConfig { - /// Stable application identity selecting its durable host catalog. Defaults to the persisted runtime compute GUID; must agree with githubEnvironment.computeId when both are supplied. - [JsonPropertyName("computeId")] - public string? ComputeId { get; set; } + /// Whether to auto-add the current working directory to readwritePaths. Default: true. + [JsonPropertyName("addCurrentWorkingDirectory")] + public bool? AddCurrentWorkingDirectory { get; set; } - /// Registers a GitHub Mission Control environment and enables its relay transport. - [JsonPropertyName("githubEnvironment")] - public HostGitHubEnvironmentOptions? GitHubEnvironment { get; set; } + /// Whether the agent may request that an individual command run outside the sandbox, which the host then approves or denies through the usual permission flow. A host capability flag rather than part of the policy: it is stripped from the effective spawn policy and only has an effect while `enabled` is true. Fail-closed, unlike the opt-out flags on this object: omitting it offers no bypass. Default: false (opt-in). + [JsonPropertyName("allowBypass")] + public bool? AllowBypass { get; set; } - /// Caller-generated UUID identifying this connection-owned listener. - [JsonPropertyName("hostId")] - public required string HostId { get; set; } + /// Whether to auto-grant read access to tool directories discovered on PATH and in toolchain environment variables (GOROOT, JAVA_HOME, VIRTUAL_ENV, and similar), and to common developer-tool caches, config, and toolchains. Writable grants cover scratch caches, the Unix GitHub CLI cache, and Cargo's registry, git store, and lock/tracker files. A relocated CARGO_HOME gets the same narrow split: registry and git are read-write; bin is read-only; the home root, config.toml, and credentials.toml stay ungranted. Set to false to disable every grant listed above; user-installed toolchains and caches then need explicit userPolicy.filesystem readonlyPaths and readwritePaths entries. The working directory (see addCurrentWorkingDirectory), temporary storage, session log paths, and system locations follow their own rules and stay granted. Default: true (enabled by default; set to false to opt out). + [JsonPropertyName("allowDevToolAccess")] + public bool? AllowDevToolAccess { get; set; } - /// Enables a local WebSocket listener. - [JsonPropertyName("localServer")] - public HostLocalServerOptions? LocalServer { get; set; } + /// Credential-injection capability flags. + [JsonPropertyName("auth")] + public SandboxConfigAuth? Auth { get; set; } - /// Ask the owning application to resume its durable AHP sessions. - [JsonPropertyName("resumeFactory")] - public bool? ResumeFactory { get; set; } + /// Opt-in whole-value environment masking for sandboxed shell, MCP, and LSP children. Configured names get random sentinels; the local proxy substitutes them only in HTTPS request headers at their injection hosts. Approved bypasses skip masking and the sandbox proxy, so bypassed shells may receive the real environment values. Disabled or explicitly opted-out routes are not protected. No credential values are stored in this configuration. + [JsonPropertyName("credentials")] + public SandboxCredentialsConfig? Credentials { get; set; } - /// Ask the owning SDK application to materialize AHP sessions. - [JsonPropertyName("sessionFactory")] - public bool? SessionFactory { get; set; } -} + /// Whether sandboxing is enabled for the session. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } -/// Empty acknowledgement for a completed host lifecycle operation. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HostDisposeResult -{ + /// The `sandboxLspServers` counterpart of `managedMcpRoutingLocked`. + [JsonInclude] + [JsonPropertyName("managedLspRoutingLocked")] + internal bool? ManagedLspRoutingLocked { get; set; } + + /// Set by the runtime when a managed policy forced `sandboxMcpServers` on and took the local opt-out away. Provenance rather than policy: it lets a sandbox startup failure point at the administrator instead of a setting the next managed merge would override, and it is ignored when comparing two configs for change. Only the managed merge may set it; a caller-supplied value is stripped. + [JsonInclude] + [JsonPropertyName("managedMcpRoutingLocked")] + internal bool? ManagedMcpRoutingLocked { get; set; } + + /// Whether language servers the session launches are confined by the sandbox. Only an explicit `false` opts out. Ignored while `enabled` is false. Default: true (enabled by default; set to false to opt out). + [JsonPropertyName("sandboxLspServers")] + public bool? SandboxLspServers { get; set; } + + /// Whether MCP servers the session launches are confined by the sandbox. Only an explicit `false` opts out; doing so also lets remote-MCP egress leave the sandbox, so the flag and `enabled` are always read together. Ignored while `enabled` is false. Default: true (enabled by default; set to false to opt out). + [JsonPropertyName("sandboxMcpServers")] + public bool? SandboxMcpServers { get; set; } + + /// User-managed sandbox policy fragment merged into the auto-discovered base policy. + [JsonPropertyName("userPolicy")] + public SandboxConfigUserPolicy? UserPolicy { get; set; } } -/// Stops a connection-owned listener and joins its teardown. +/// Identifies the credential hosts that the persistent certificate authority of the sandbox credential proxy must cover. The runtime always adds the hosts from the saved user settings. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HostDisposeRequest +internal sealed class SandboxProxyCaRequest { - /// Listener UUID. Unknown or successfully stopped IDs are harmless. - [JsonPropertyName("hostId")] - public string HostId { get; set; } = string.Empty; + /// The sandbox configuration that the host gives its sessions. The runtime reads the credential hosts from `auth` and `credentials`; it ignores `enabled` and the other fields. + [JsonPropertyName("sandboxConfig")] + public SandboxConfig? SandboxConfig { get; set; } } -/// Private credentials delivered only to a runtime-owned Mission Control hosting participant. +/// Status of the persistent certificate authority of the sandbox credential proxy. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HostGetEnvironmentCredentialsResult +public sealed class SandboxProxyCaStatus { - /// GitHub API base URL for the authenticated service. - [JsonPropertyName("githubApiUrl")] - public string GitHubApiUrl { get; set; } = string.Empty; - - /// Hostname of the authenticated GitHub service. - [JsonPropertyName("githubHost")] - public string GitHubHost { get; set; } = string.Empty; + /// Whether this process can add the certificate authority to OS trust without credentials from a different user. False where OS trust is unsupported, and on Windows when the process cannot elevate itself to write the machine trust store. When false, do not offer to set up the certificate authority. + [JsonPropertyName("canInstall")] + public bool CanInstall { get; set; } - /// Mission Control API origin for environment registration and management. - [JsonPropertyName("missionControlUrl")] - public string MissionControlUrl { get; set; } = string.Empty; + /// Human-readable reason for the state. On `installed` or `notInstalled`, present only when the certificate authority must be rotated, and then says why. + [JsonPropertyName("detail")] + public string? Detail { get; set; } - /// Current bearer token for the authenticated GitHub identity. - [JsonPropertyName("token")] - public string Token { get; set; } = string.Empty; + /// The state of the certificate authority. + [JsonPropertyName("state")] + public SandboxProxyCaState State { get; set; } } -/// Normalized local WebSocket listener settings. +/// Result of creating the persistent certificate authority of the sandbox credential proxy. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HostLocalServerConfiguration +public sealed class SandboxProxyCaCreateResult { - /// Hostname or IP address to bind. - [JsonPropertyName("hostname")] - public string Hostname { get; set; } = string.Empty; - - /// Port to bind, with zero requesting OS allocation. - [JsonPropertyName("port")] - public int Port { get; set; } + /// Absolute path of the public certificate of the certificate authority, in PEM format. + [JsonPropertyName("certificatePath")] + public string CertificatePath { get; set; } = string.Empty; +} - /// Whether the listener requires token authentication. - [JsonPropertyName("requireConnectionToken")] - public bool RequireConnectionToken { get; set; } +/// Where this runtime keeps the sandbox proxy password. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SandboxProxySecretLocation +{ + /// Absolute path of the credential store holding the sandbox proxy password. The proxy password belongs to no MCP server, so it is kept in its own directory rather than in the MCP secret store. + [JsonPropertyName("configDir")] + public string ConfigDir { get; set; } = string.Empty; - /// Secret connection token, absent when authentication is disabled. - [JsonPropertyName("token")] - public string? Token { get; set; } + /// Identifier of the proxy password entry within that store. Derived from the configuration directory, so two Copilot homes on one machine never share an entry. Use it to recognize a `${secret:…}` reference in stored settings as this runtime's own proxy password. + [JsonPropertyName("secretId")] + public string SecretId { get; set; } = string.Empty; } -/// Normalized listener settings delivered only to the supervised hosting participant. +/// The stored sandbox proxy password, if one is present. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HostGetConfigurationResult +internal sealed class SandboxProxySecretGetResult { - /// Requested GitHub Mission Control registration. - [JsonPropertyName("githubEnvironment")] - public HostGitHubEnvironmentOptions? GitHubEnvironment { get; set; } - - /// Normalized local listener settings, absent for relay-only hosts. - [JsonPropertyName("localServer")] - public HostLocalServerConfiguration? LocalServer { get; set; } + /// The stored password. Absent when no entry exists in either the keychain or the on-disk fallback store. + [JsonPropertyName("value")] + public string? Value { get; set; } +} - /// Whether app-owned durable sessions are resumed by the owning application. - [JsonPropertyName("resumeFactory")] - public bool ResumeFactory { get; set; } +/// Where the sandbox proxy password was stored. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SandboxProxySecretSaveResult +{ + /// Whether the OS keychain accepted the value. False means it was written to the on-disk fallback store instead, which is still readable by `sandbox.proxySecret.get`. + [JsonPropertyName("storedInKeychain")] + public bool StoredInKeychain { get; set; } +} - /// Whether session materialization is delegated to the owning application. - [JsonPropertyName("sessionFactory")] - public bool SessionFactory { get; set; } +/// The sandbox proxy password to store. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SandboxProxySecretSaveRequest +{ + /// The password to store for the sandbox proxy. + [JsonPropertyName("value")] + public string Value { get; set; } = string.Empty; } -/// Empty acknowledgement for a completed host lifecycle operation. +/// The stored sandbox settings to resolve against the defaults. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HostReadyResult +internal sealed class SandboxApplyDefaultsRequest { + /// The sandbox settings as stored in the settings file. An empty object resolves to the defaults alone. + [JsonPropertyName("stored")] + public JsonElement Stored { get; set; } } -/// Readiness reported by the supervised hosting participant on its own SDK connection. +/// The outcome of a locked snapshot write. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HostReadyRequest +internal sealed class PersistenceWriteRawJsonLockedResult { - /// Actual bound WebSocket URL. - [JsonPropertyName("address")] - public string? Address { get; set; } - - /// Registered environment ID, reported only once the relay transport is connected. - [JsonPropertyName("environmentId")] - public string? EnvironmentId { get; set; } + /// Why the write failed. Absent when `success` is true. A lock the runtime could not acquire in time reports here rather than raising. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Configured secret token, absent when authentication is disabled. - [JsonPropertyName("token")] - public string? Token { get; set; } + /// Whether the file now holds the requested contents. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// One server-discovered hook action from user, repository, plugin, or managed-policy configuration. +/// A complete JSON snapshot to write under the runtime's per-path file lock. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DiscoveredHook +internal sealed class PersistenceWriteRawJsonLockedRequest { - /// Durable content hash used by hook enablement. Identical actions may intentionally share this key. Omitted when changing the user's disabled-hooks setting cannot change the action's current server-discovered state, including managed-policy hooks, session-start prompt actions, actions suppressed by disable-all settings, and projectless plugin actions that require project-directory expansion. - [JsonPropertyName("disableKey")] - public string? DisableKey { get; set; } - - /// Whether this action is enabled under the server-side discovery settings. Concrete sessions may differ because they can add session-specific directories, plugins, or trust. False when its disable key is present in the user's disabled-hooks setting or disable-all settings suppress the action. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } - - /// Hook event that invokes this action. - [JsonPropertyName("hookType")] - public HookType HookType { get; set; } + /// The serialized JSON document to write. It replaces the file's contents entirely; nothing is merged. + [JsonPropertyName("dataJson")] + public string DataJson { get; set; } = string.Empty; - /// Deterministic identifier for this server-discovered action row. It remains stable while the project, origin, source, event, action content, and duplicate ordinal are unchanged. This is row identity, not the key persisted in disabledHooks. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Comment lines to emit above the document, for a format that tolerates them. Omit for strict JSON. + [JsonPropertyName("fileHeader")] + public string? FileHeader { get; set; } - /// Configuration tier that contributed this hook action. - [JsonPropertyName("origin")] - public HookOrigin Origin { get; set; } + /// Absolute path of the file to replace. Parent directories are created when missing. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; +} - /// Input project path for which this server-side action was resolved. Set on every row returned for project-scoped discovery, including repeated user and policy actions. - [JsonPropertyName("projectPath")] - public string? ProjectPath { get; set; } +/// Outcome of rewriting the `disabledHooks` setting. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class UserSettingsSetHookDisabledResult +{ + /// Why the write failed. Absent when the status is `ok`. + [JsonPropertyName("errorMessage")] + public string? ErrorMessage { get; set; } - /// Human-readable source label, such as a hook file path, settings source, or plugin name. - [JsonPropertyName("source")] - public string? Source { get; set; } + /// `ok` when the setting was written, `invalid` when settings.json could not be parsed and was left alone, `error` when the write itself failed. + [JsonPropertyName("status")] + public string Status { get; set; } = string.Empty; } -/// Server-discovered hook actions and partial-load diagnostics from user, repository, plugin, and managed-policy sources. Concrete sessions may include additional session-specific hook sources. +/// One hook to disable or re-enable. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HooksDiscoverResult +internal sealed class UserSettingsSetHookDisabledRequest { - /// Errors for hook sources or actions that could not be loaded, making the result partially incomplete. Other valid actions are still returned. Project-resolution and repository-settings errors are prefixed with their project path. - [JsonPropertyName("errors")] - public IList Errors { get => field ??= []; set; } + /// True adds the key to `disabledHooks`, false removes it. + [JsonPropertyName("disabled")] + public bool Disabled { get; set; } - /// All discovered hook actions. Byte-identical actions remain separate rows even when they share a disable key. - [JsonPropertyName("hooks")] - public IList Hooks { get => field ??= []; set; } + /// Durable content-hash key of the hook, as `hooks.loadResources` reports it. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("disableKey")] + public string DisableKey { get; set; } = string.Empty; - /// Non-fatal source-loading warnings. Discovery remains complete for the affected source, although the source had a recoverable issue. Repository-settings warnings are prefixed with their project path when attribution is available. - [JsonPropertyName("warnings")] - public IList Warnings { get => field ??= []; set; } + /// Path of the settings.json to rewrite. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("settingsPath")] + public string SettingsPath { get; set; } = string.Empty; } -/// Optional project paths and host-exclusion behavior for server-scoped hook discovery. +/// Selects the configuration directory whose user settings to read. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HooksDiscoverRequest +internal sealed class UserSettingsLoadForConfigDirRequest { - /// When true, omit host-owned user and plugin hook rows and their diagnostics. Managed-policy hooks and trusted repository hooks remain visible, and host disabledHooks still contribute to each remaining row's effective enabled state. This filters sources rather than simulating a host with no settings. - [JsonPropertyName("excludeHostHooks")] - public bool? ExcludeHostHooks { get; set; } - - /// Optional project directory paths whose trusted repository and project-expanded plugin hooks should be discovered. When omitted or empty, user, managed-policy, and globally enabled installed or explicit plugin hooks are returned without project expansion. - [JsonPropertyName("projectPaths")] - public IList? ProjectPaths { get; set; } + /// Copilot configuration directory to read the settings document from, taking precedence over the server's own `COPILOT_HOME` and default home. Omit it, or pass an empty string, to read the directory the server resolved for itself. + [JsonPropertyName("configDir")] + public string? ConfigDir { get; set; } } -/// Active server-driven promotion for a model, including its discount and optional expiry. +/// A repository's settings as stored, together with any diagnostic raised while reading them. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelBillingPromo +internal sealed class UserSettingsLoadRepoResult { - /// Percentage discount (0-100) applied while the promotion is active. May be fractional. - [JsonPropertyName("discountPercent")] - public double? DiscountPercent { get; set; } - - /// UTC ISO 8601 timestamp marking when the promotion ends. Optional: an open-ended promotion omits this field. When present, the API only surfaces a promo whose expiry parses and is in the future, so consumers should treat a past value as expired. - [JsonPropertyName("endsAt")] - public string? EndsAt { get; set; } - - /// Stable identifier for the promotion campaign. - [JsonPropertyName("id")] - public string? Id { get; set; } + /// The repository's settings document, keyed by setting name, with each value carried verbatim. Null when the repository declares no settings. + [JsonPropertyName("settings")] + public IDictionary? Settings { get; set; } - /// Human-readable promotion message. Does not include the expiry timestamp; consumers may format endsAt and append it when present. - [JsonPropertyName("message")] - public string? Message { get; set; } + /// Why part of the document was ignored, as a single human-readable string joining every diagnostic with `; `. Null when the document loaded cleanly. The settings that did load are still reported, so a consumer should surface this and continue rather than treat it as a failure. + [JsonPropertyName("warning")] + public string? Warning { get; set; } +} - /// Whether the service asked hosts to give this promotion a prominent surface, such as a dedicated banner, in addition to listing it with the model. `true` requests that surface and `false` asks for the model list only. Absent means the service expressed no preference — for example a response that predates the field — so hosts should apply their own default rather than read it as `false`. - [JsonPropertyName("showBanner")] - public bool? ShowBanner { get; set; } +/// Selects the repository whose checked-in settings to read. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class UserSettingsLoadRepoRequest +{ + /// Absolute path of the repository root whose settings to read. When this is the user's home directory, the Claude-compatible configuration is skipped, so a home-directory session does not inherit a personal config as if it were repository policy. + [JsonPropertyName("repoRoot")] + public string RepoRoot { get; set; } = string.Empty; } -/// Long context tier pricing (available for models with extended context windows). +/// The settings a consumer should act on inside a repository, together with any diagnostic raised while reading the repository layer. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelBillingTokenPricesLongContext +internal sealed class UserSettingsLoadAndMergeRepoResult { - /// Use cacheReadPrice instead. AI Credits cost per billing batch of cached tokens. - [EditorBrowsable(EditorBrowsableState.Never)] -#if NET5_0_OR_GREATER - [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] -#else - [Obsolete("This member is deprecated and will be removed in a future version.")] -#endif - [JsonPropertyName("cachePrice")] - public double? CachePrice { get; set; } + /// The merged settings document, keyed by setting name. Equals the supplied user layer when the repository declares no settings. + [JsonPropertyName("settings")] + public IDictionary Settings { get => field ??= new Dictionary(); set; } - /// AI Credits cost per billing batch of cached (read) tokens. - [JsonPropertyName("cacheReadPrice")] - public double? CacheReadPrice { get; set; } + /// Why part of the repository document was ignored, as a single human-readable string joining every diagnostic with `; `. Null when it loaded cleanly. The settings that did load are still merged, so a consumer should surface this and continue rather than treat it as a failure. + [JsonPropertyName("warning")] + public string? Warning { get; set; } +} - /// AI Credits cost per billing batch of 1-hour cache-write (cache creation) tokens. - [JsonPropertyName("cacheWrite1hPrice")] - public double? CacheWrite1hPrice { get; set; } +/// A repository to read settings from, and the user layer they are merged over. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class UserSettingsLoadAndMergeRepoRequest +{ + /// Absolute path of the repository root whose settings to read. When this is the user's home directory, the Claude-compatible configuration is skipped, so a home-directory session does not inherit a personal config as if it were repository policy. + [JsonPropertyName("repoRoot")] + public string RepoRoot { get; set; } = string.Empty; - /// AI Credits cost per billing batch of cache-write (cache creation) tokens. - [JsonPropertyName("cacheWritePrice")] - public double? CacheWritePrice { get; set; } + /// The user's own settings, keyed by setting name. The repository layer is merged over this; a repository may only add to the collection settings it is allowed to influence, so a user setting it does not name survives unchanged. + [JsonPropertyName("userSettings")] + public IDictionary UserSettings { get => field ??= new Dictionary(); set; } +} - /// Use maxPromptTokens instead. Prompt token budget for the long context tier. The total context window is this value plus the model's max_output_tokens. - [EditorBrowsable(EditorBrowsableState.Never)] -#if NET5_0_OR_GREATER - [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] -#else - [Obsolete("This member is deprecated and will be removed in a future version.")] -#endif - [JsonPropertyName("contextMax")] - public long? ContextMax { get; set; } +/// The on-disk location of the user's settings document. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class UserSettingsPathResult +{ + /// Absolute path to the settings file this runtime reads and writes. Normally `settings.json` under the Copilot configuration directory; an extensionless legacy `settings` file is reported instead when it exists and `settings.json` does not. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; +} - /// AI Credits cost per billing batch of input tokens. - [JsonPropertyName("inputPrice")] - public double? InputPrice { get; set; } +/// The user's stored settings, together with any diagnostic raised while reading them. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class UserSettingsLoadWithWarningResult +{ + /// The stored user settings document, keyed by setting name, with each value carried verbatim. Empty when the file could not be read at all. + [JsonPropertyName("settings")] + public IDictionary Settings { get => field ??= new Dictionary(); set; } - /// Prompt token budget for the long context tier. The total context window is this value plus the model's max_output_tokens. - [JsonPropertyName("maxPromptTokens")] - public long? MaxPromptTokens { get; set; } + /// Why part of the document was ignored, phrased for display to the user. Null when the document loaded cleanly. The settings that did load are still reported, so a consumer may surface this and continue rather than treat it as a failure. + [JsonPropertyName("warning")] + public string? Warning { get; set; } +} - /// AI Credits cost per billing batch of output tokens. - [JsonPropertyName("outputPrice")] - public double? OutputPrice { get; set; } +/// The user layer folded over the managed layer. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class UserSettingsMergeManagedAndUserResult +{ + /// The merged settings. An empty object when neither layer was supplied, so an unmanaged host with no user settings governs nothing. + [JsonPropertyName("settingsJson")] + public JsonElement SettingsJson { get; set; } } -/// Token-level pricing information for this model. +/// The managed (enterprise policy) layer and the user layer to fold together. The field names carry the layer identity: nothing about a settings object identifies which layer produced it, so a generic base/override pair would let a caller transpose them and silently resolve the governance keys in the user's favour. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelBillingTokenPrices +internal sealed class UserSettingsMergeManagedAndUserParams { - /// Number of tokens per standard billing batch. - [JsonPropertyName("batchSize")] - public long? BatchSize { get; set; } + /// The managed layer. Its governance keys win, and the per-entry `enabledPlugins` and `extraKnownMarketplaces` control maps are stamped back from it after the union. Omitted when the host is unmanaged. + [JsonPropertyName("managed")] + public JsonElement? Managed { get; set; } - /// Use cacheReadPrice instead. AI Credits cost per billing batch of cached tokens. - [EditorBrowsable(EditorBrowsableState.Never)] -#if NET5_0_OR_GREATER - [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] -#else - [Obsolete("This member is deprecated and will be removed in a future version.")] -#endif - [JsonPropertyName("cachePrice")] - public double? CachePrice { get; set; } + /// The user's own settings layer. Wins on every key the managed layer does not govern. Omitted when the user has no settings yet. + [JsonPropertyName("user")] + public JsonElement? User { get; set; } +} - /// AI Credits cost per billing batch of cached (read) tokens. - [JsonPropertyName("cacheReadPrice")] - public double? CacheReadPrice { get; set; } +/// The repository layer folded into the user layer. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class UserSettingsMergeRepoValueResult +{ + /// The merged settings, with the per-entry managed control maps re-stamped from the runtime's ambient managed policy. + [JsonPropertyName("settingsJson")] + public JsonElement SettingsJson { get; set; } +} - /// AI Credits cost per billing batch of 1-hour cache-write (cache creation) tokens. - [JsonPropertyName("cacheWrite1hPrice")] - public double? CacheWrite1hPrice { get; set; } +/// The user layer and the repository layer to fold together. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class UserSettingsMergeRepoValueParams +{ + /// The repository's checked-in settings layer. + [JsonPropertyName("repoSettings")] + public JsonElement RepoSettings { get; set; } - /// AI Credits cost per billing batch of cache-write (cache creation) tokens. - [JsonPropertyName("cacheWritePrice")] - public double? CacheWritePrice { get; set; } + /// The user's settings, already resolved against any managed layer. + [JsonPropertyName("userSettings")] + public JsonElement UserSettings { get; set; } +} - /// Use maxPromptTokens instead. Prompt token budget for the default tier. The total context window is this value plus the model's max_output_tokens. - [EditorBrowsable(EditorBrowsableState.Never)] -#if NET5_0_OR_GREATER - [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] -#else - [Obsolete("This member is deprecated and will be removed in a future version.")] -#endif - [JsonPropertyName("contextMax")] - public long? ContextMax { get; set; } +/// The outcome of the one-time staff update-channel migration. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class UserSettingsMigrateStaffUpdateChannelOnceResult +{ + /// When the migration was recorded as done, as an ISO-8601 timestamp. Absent only for `ineligible`, which records nothing. + [JsonPropertyName("completedAt")] + public string? CompletedAt { get; set; } - /// AI Credits cost per billing batch of input tokens. - [JsonPropertyName("inputPrice")] - public double? InputPrice { get; set; } + /// What the migration did. Only `migrated` means the persisted settings changed. + [JsonPropertyName("status")] + public StaffUpdateChannelMigrationStatus Status { get; set; } +} - /// Long context tier pricing (available for models with extended context windows). - [JsonPropertyName("longContext")] - public ModelBillingTokenPricesLongContext? LongContext { get; set; } +/// The filesystem and environment facts a user-settings file is resolved from. Passed explicitly rather than read from the runtime's own process, so a host whose configuration directory differs from its process environment resolves the file it means. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class UserSettingsHostContext +{ + /// The Copilot configuration directory, when the host set one. Takes precedence over `COPILOT_HOME` and the home directory. + [JsonPropertyName("configDir")] + public string? ConfigDir { get; set; } - /// Prompt token budget for the default tier. The total context window is this value plus the model's max_output_tokens. - [JsonPropertyName("maxPromptTokens")] - public long? MaxPromptTokens { get; set; } + /// The host's environment variables. `COPILOT_HOME` is read from here, as are the variables a settings hook may expand. + [JsonPropertyName("environment")] + public IDictionary Environment { get => field ??= new Dictionary(); set; } - /// AI Credits cost per billing batch of output tokens. - [JsonPropertyName("outputPrice")] - public double? OutputPrice { get; set; } + /// The user's home directory, used when neither a configuration directory nor `COPILOT_HOME` is set. + [JsonPropertyName("homeDirectory")] + public string HomeDirectory { get; set; } = string.Empty; } -/// Billing information. +/// The outcome of the one-time staff model-selection reset. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelBilling +internal sealed class UserSettingsResetStaffModelSelectionOnceResult { - /// Whole-number percentage discount (0-100) applied to usage billed through this model. Populated for the synthetic `auto` model, where requests routed by auto-mode are billed at a reduced rate; absent for concrete models. - [JsonPropertyName("discountPercent")] - public int? DiscountPercent { get; set; } + /// The cutoff this reset is gated on, as an ISO-8601 timestamp. A recorded timestamp at or after it means the reset is already done. + [JsonPropertyName("cutoff")] + public string Cutoff { get; set; } = string.Empty; - /// Billing cost multiplier relative to the base rate. - [JsonPropertyName("multiplier")] - public double? Multiplier { get; set; } + /// Whether the persisted model selection was cleared. Only `true` means the settings document changed and the host should adopt the returned settings. + [JsonPropertyName("didReset")] + public bool DidReset { get; set; } - /// Active server-driven promotion for this model, if any. Present when the model is being promoted with a discount, which may be time-boxed or open-ended. - [JsonPropertyName("promo")] - public ModelBillingPromo? Promo { get; set; } + /// The timestamp the host should now record, as an ISO-8601 timestamp. Carries the caller's own value back unchanged when nothing was reset. + [JsonPropertyName("resetAt")] + public string? ResetAt { get; set; } - /// Token-level pricing information for this model. - [JsonPropertyName("tokenPrices")] - public ModelBillingTokenPrices? TokenPrices { get; set; } + /// The settings document as it stands after the reset, keyed by setting name. Present only when `didReset` is true. + [JsonPropertyName("settings")] + public IDictionary? Settings { get; set; } } -/// Vision-specific limits. +/// Selects the configuration directory to run the one-time staff model-selection reset against, and supplies the caller's record of whether it has already run. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelCapabilitiesLimitsVision +internal sealed class UserSettingsResetStaffModelSelectionOnceRequest { - /// Maximum image size in bytes. - [JsonPropertyName("max_prompt_image_size")] - public long MaxPromptImageSize { get; set; } + /// The configuration directory, home directory and environment to resolve the settings and global-state documents from. A host that scopes a session to its own Copilot home must pass that directory here, or the reset is evaluated against the server's own home instead. + [JsonPropertyName("context")] + public UserSettingsHostContext Context { get => field ??= new(); set; } - /// Maximum number of images per prompt. - [JsonPropertyName("max_prompt_images")] - public long MaxPromptImages { get; set; } + /// Whether the signed-in account is a staff account. The reset applies only to staff, so anything other than `true` returns `didReset: false` without touching the settings document. + [JsonPropertyName("staff")] + public bool? Staff { get; set; } - /// MIME types the model accepts. - [JsonPropertyName("supported_media_types")] - public IList SupportedMediaTypes { get => field ??= []; set; } + /// When the caller last recorded this reset, as an ISO-8601 timestamp. A timestamp at or after the cutoff means the reset already ran and is returned unchanged. + [JsonPropertyName("staffModelResetAt")] + public string? StaffModelResetAt { get; set; } } -/// Token limits for prompts, outputs, and context window. +/// The unrecognized top-level keys found in the user settings document, already phrased for display. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelCapabilitiesLimits +internal sealed class UserSettingsUnknownKeysWarningResult { - /// Maximum total context window size in tokens. - [JsonPropertyName("max_context_window_tokens")] - public long? MaxContextWindowTokens { get; set; } - - /// Maximum number of output/completion tokens. - [JsonPropertyName("max_output_tokens")] - public long? MaxOutputTokens { get; set; } - - /// Maximum number of prompt/input tokens. - [JsonPropertyName("max_prompt_tokens")] - public long? MaxPromptTokens { get; set; } - - /// Vision-specific limits. - [JsonPropertyName("vision")] - public ModelCapabilitiesLimitsVision? Vision { get; set; } + /// The warning to show the user, naming the unrecognized keys and the file they were found in. Absent when every top-level key is recognized, and also when the document is missing or could not be read, because an unknown-key list taken from a document the runtime could not load would name keys that may not be there. + [JsonPropertyName("warning")] + public string? Warning { get; set; } } -/// Feature flags indicating what the model supports. +/// Selects the configuration directory whose user settings document should be inspected for unrecognized top-level keys. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelCapabilitiesSupports +internal sealed class UserSettingsUnknownKeysWarningRequest { - /// Resolved Anthropic adaptive-thinking capability — unsupported / optional / required / adaptive_only. 'required' models reject thinking.type='enabled' with HTTP 400 but still accept 'disabled' (e.g. opus-4.7/4.8/5, sonnet-5); 'adaptive_only' models accept nothing but 'adaptive' (e.g. fable, mythos). - [JsonPropertyName("adaptive_thinking")] - public AdaptiveThinkingSupport? AdaptiveThinking { get; set; } + /// The configuration directory, home directory and environment to resolve the settings document from. A host that scopes a session to its own Copilot home must pass that directory here, or the server's own home is inspected instead. + [JsonPropertyName("context")] + public UserSettingsHostContext Context { get => field ??= new(); set; } +} - /// Whether this model supports reasoning effort configuration. - [JsonPropertyName("reasoningEffort")] - public bool? ReasoningEffort { get; set; } +/// The overrides and host context to resolve effective runtime settings from. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class UserSettingsBuildRuntimeRequest +{ + /// The environment to read settings-bearing variables from. Passed explicitly rather than read from the server process so a host can resolve settings for an environment it is about to create rather than the one it runs in. + [JsonPropertyName("environment")] + public IDictionary Environment { get => field ??= new Dictionary(); set; } - /// Whether the model supports provider-native thinking. Independent of configurable reasoning effort; omission means unknown. - [JsonPropertyName("thinking")] - public bool? Thinking { get; set; } + /// Settings that take precedence over every document and environment layer, such as the values a host derived from its own command line. Keyed by setting name, with each value carried verbatim, so a host may pass keys this runtime does not recognize. Absent when the host contributes no overrides of its own. + [JsonPropertyName("overrides")] + public IDictionary? Overrides { get; set; } - /// Whether this model supports canonical tool calling. - [JsonPropertyName("toolCalls")] - public bool? ToolCalls { get; set; } + /// The platform to apply platform-specific defaults for, in the same spelling the host's own runtime reports. + [JsonPropertyName("platform")] + public string Platform { get; set; } = string.Empty; - /// Whether this model supports vision/image input. - [JsonPropertyName("vision")] - public bool? Vision { get; set; } + /// The directory the settings are being resolved for. This selects the repository-scoped document, so a host resolving settings for a directory other than its own process working directory must pass that directory here. + [JsonPropertyName("workingDirectory")] + public string WorkingDirectory { get; set; } = string.Empty; } -/// Model capabilities and limits. +/// The outcome of the one-time staff log-level migration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelCapabilities +internal sealed class UserSettingsMigrateStaffLogLevelOnceResult { - /// Token limits for prompts, outputs, and context window. - [JsonPropertyName("limits")] - public ModelCapabilitiesLimits? Limits { get; set; } + /// When the migration was recorded as done, as an ISO-8601 timestamp. Absent only for `ineligible`, which records nothing. + [JsonPropertyName("completedAt")] + public string? CompletedAt { get; set; } - /// Feature flags indicating what the model supports. - [JsonPropertyName("supports")] - public ModelCapabilitiesSupports? Supports { get; set; } + /// The settings document as it stands after the migration, keyed by setting name. Present only for `refreshed`, and only when the document could be re-read cleanly. + [JsonPropertyName("settings")] + public IDictionary? Settings { get; set; } + + /// What the migration did. Only `refreshed` can have changed the persisted settings. + [JsonPropertyName("status")] + public StaffLogLevelMigrationStatus Status { get; set; } } -/// A service-published message about a model, carrying a stable machine-readable code alongside human-readable text. +/// Selects the configuration directory to run the one-time staff log-level migration against, and supplies the caller's record of whether it has already run. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelMessage +internal sealed class UserSettingsMigrateStaffLogLevelOnceRequest { - /// Stable machine-readable identifier for the message, such as `client_version_deprecated`. Hosts can key custom presentation off this; unrecognized codes should fall back to displaying `message`. - [JsonPropertyName("code")] - public string Code { get; set; } = string.Empty; + /// When the caller last recorded this migration, as an ISO-8601 timestamp. Supplying it lets the runtime answer `current` without re-running the evaluation. + [JsonPropertyName("cachedCompletedAt")] + public string? CachedCompletedAt { get; set; } - /// Human-readable message text intended for display to the user. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; + /// The configuration directory, home directory and environment to resolve the settings and global-state documents from. A host that scopes a session to its own Copilot home must pass that directory here, or the migration is evaluated against the server's own home instead. + [JsonPropertyName("context")] + public UserSettingsHostContext Context { get => field ??= new(); set; } } -/// Policy state (if applicable). +/// The outcome of the one-time obsolete-settings cleanup. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelPolicy +internal sealed class UserSettingsMigrateObsoleteOnDiskResult { - /// Current policy state for this model. - [JsonPropertyName("state")] - public ModelPolicyState State { get; set; } - - /// Usage terms or conditions for this model. - [JsonPropertyName("terms")] - public string? Terms { get; set; } + /// What the cleanup did. Only `changed` rewrote the document. The cleaned settings are not returned here: the document is written before this replies, so `user.settings.load` reads it authoritatively. + [JsonPropertyName("status")] + public UserSettingsMigrateObsoleteOnDiskStatus Status { get; set; } } -/// Service-published warning text that hosts should display when presenting a model. +/// Selects the configuration directory whose settings document to clean. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelWarningText +internal sealed class UserSettingsMigrateObsoleteOnDiskRequest { - /// Data-retention warning for the model. The text may contain Markdown links and should be rendered as Markdown when supported. - [JsonPropertyName("dataRetention")] - public string? DataRetention { get; set; } + /// The configuration directory holding the settings document to clean. A host that scopes a session to its own Copilot home passes that directory here; omit it to clean the document under the home the server resolved for itself. The home directory and environment are deliberately not caller-supplied: they decide only the settings path root and the two hook-parsing flags, and those describe the process doing the reading rather than the caller. + [JsonPropertyName("configDir")] + public string? ConfigDir { get; set; } } -/// Copilot model metadata, including identifier, display name, capabilities, policy, billing, reasoning efforts, and picker categories. +/// A single user setting's effective value alongside its default, so consumers can render settings left at their default. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class Model +public sealed class UserSettingMetadata { - /// Billing information. - [JsonPropertyName("billing")] - public ModelBilling? Billing { get; set; } - - /// Model capabilities and limits. - [JsonPropertyName("capabilities")] - public ModelCapabilities Capabilities { get => field ??= new(); set; } - - /// Default reasoning effort level (only present if model supports reasoning effort). - [JsonPropertyName("defaultReasoningEffort")] - public string? DefaultReasoningEffort { get; set; } - - /// Model identifier (e.g., "claude-sonnet-4.5"). - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; - - /// Informational notices the service published for this model, such as an upcoming change or a recommended alternative. Present only when the service published at least one notice. Hosts should surface these without implying anything is wrong with the model. - [JsonPropertyName("infoMessages")] - public IList? InfoMessages { get; set; } - - /// Provider-supplied model metadata. Keys and JSON-compatible values are preserved unchanged. This is factual metadata published by the model provider; it carries no picker or UX semantics. - [JsonPropertyName("metadata")] - public IDictionary? Metadata { get; set; } - - /// Model capability category for grouping in the model picker. - [JsonPropertyName("modelPickerCategory")] - public ModelPickerCategory? ModelPickerCategory { get; set; } - - /// Relative cost tier for token-based billing users. - [JsonPropertyName("modelPickerPriceCategory")] - public ModelPickerPriceCategory? ModelPickerPriceCategory { get; set; } - - /// Display name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - /// Policy state (if applicable). - [JsonPropertyName("policy")] - public ModelPolicy? Policy { get; set; } - - /// The model provider that produced this model, as a neutral reference (opaque id, human-readable label, and provider kind). Present on models returned by `session.model.list`, which resolves provider attribution from the session's account roster; absent on the flat `server.models.list`, which does not resolve a session roster. The model picker groups by this reference. - [JsonPropertyName("provider")] - public ModelProviderRef? Provider { get; set; } - - /// Context-window tiers this model offers, when the provider advertises them independently of tiered token pricing. Copilot models carry their tiers in `billing.tokenPrices`; a provider that has no pricing to publish (an agent host reached over AHP, for example) declares them here instead, so the model picker can still offer the tier toggle. - [JsonPropertyName("supportedContextTiers")] - public IList? SupportedContextTiers { get; set; } - - /// Supported reasoning effort levels (only present if model supports reasoning effort). - [JsonPropertyName("supportedReasoningEfforts")] - public IList? SupportedReasoningEfforts { get; set; } - - /// Model vendor as the Copilot API reports it, for example "Anthropic" or "Azure OpenAI". Open vocabulary, passed through unchanged. It can name the vendor that serves the model instead of the one that built it, or a label that is not a vendor, such as "Experimental". Absent when the Copilot API reports no vendor. - [JsonPropertyName("vendor")] - public string? Vendor { get; set; } + /// The centrally-known default for this setting (null when no default is registered). + [JsonPropertyName("default")] + public JsonElement Default { get; set; } - /// Warnings the service published for this model, such as a deprecated client version. Present only when the service published at least one warning. The model remains usable; hosts should surface these as advisory rather than blocking. - [JsonPropertyName("warningMessages")] - public IList? WarningMessages { get; set; } + /// True when the user has not set an explicit value for this setting (i.e. it is left at its default). Reflects whether the user has overridden the key, not whether the effective value happens to equal the default — a key explicitly set to a value identical to the default still reports false. + [JsonPropertyName("isDefault")] + public bool IsDefault { get; set; } - /// Warning text the service requires hosts to surface for this model. Present only when the service published at least one warning. - [JsonPropertyName("warningText")] - public ModelWarningText? WarningText { get; set; } + /// The effective value: the user's value if set, otherwise the default. + [JsonPropertyName("value")] + public JsonElement Value { get; set; } } -/// List of Copilot models available to the resolved user, including capabilities and billing metadata. +/// Per-key metadata for every known user setting in settings.json, including settings left at their default. Excludes repository- and enterprise-managed overrides. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelList +public sealed class UserSettingsGetResult { - /// List of available models with full metadata. - [JsonPropertyName("models")] - public IList Models { get => field ??= []; set; } + /// Every known user setting keyed by setting name, each with its effective value, default, and whether it is at the default. + [JsonPropertyName("settings")] + public IDictionary Settings { get => field ??= new Dictionary(); set; } } -/// RPC data type for ModelsList operations. +/// On-disk locations of the settings files this runtime reads. A path is reported whether or not the file exists. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelsListRequest +internal sealed class UserSettingsGetPathsResult { - /// GitHub token accepted for compatibility with existing SDK clients. When provided, resolves this token instead of using the current account. - [JsonPropertyName("gitHubToken")] - public string? GitHubToken { get; set; } + /// The repository's local, untracked settings file, or null when no repository root was supplied. + [JsonPropertyName("repoLocalSettings")] + public string? RepoLocalSettings { get; set; } - /// Opaque account identifier returned by `account.getAllUsers`. When omitted, the current account is used. - [JsonPropertyName("selectionId")] - public string? SelectionId { get; set; } + /// The repository's checked-in settings file, or null when no repository root was supplied. + [JsonPropertyName("repoSettings")] + public string? RepoSettings { get; set; } + + /// The user settings file: `settings.json` under the resolved Copilot home. This is the file `user.settings.get`, `user.settings.load`, and `user.settings.set` read and write. + [JsonPropertyName("userSettings")] + public string UserSettings { get; set; } = string.Empty; } -/// A well-known model in the runtime's built-in catalog. +/// Selects which settings file paths to resolve. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class BuiltInModelCatalogEntry +internal sealed class UserSettingsGetPathsRequest { - /// Well-known runtime model ID suitable for provider or provider-model metadata. This is not necessarily the provider-facing deployment or model name and does not indicate CAPI entitlement or provider availability. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Absolute path of a repository root. Supply it to also resolve that repository's two settings file paths; omit it to resolve only the user path. + [JsonPropertyName("repoRoot")] + public string? RepoRoot { get; set; } } -/// The running runtime's complete catalog of well-known built-in model IDs, including supported models and additional IDs with built-in metadata. +/// Partial user settings to write to settings.json. Each top-level key is written individually, replacing the existing value; a key whose value is null is removed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class BuiltInModelCatalog +internal sealed class UserSettingsSetRequest { - /// Built-in model entries. - [JsonPropertyName("models")] - public IList Models { get => field ??= []; set; } + /// Partial user settings to write, as a free-form object keyed by setting name. + [JsonPropertyName("settings")] + public JsonElement Settings { get; set; } } -/// Whether this host can run one sandbox policy feature. A session whose effective policy uses an unsupported feature fails each sandboxed command with `reason`, except `filesystem_enumeration`, whose absence degrades sandboxed PowerShell instead of failing it. +/// Top-level user-setting keys grouped by who may govern them. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxHostCapability +internal sealed class UserSettingsGovernanceKeysResult { - /// The policy feature, as an extensible string: ignore names you do not recognize. Known values: `network` (sandboxed commands can reach the network; on Linux this needs the tooling for Bubblewrap's private network namespace, such as slirp4netns), `network_filtering` (host rules and the sandbox proxy; on Linux this needs the same tooling as `network`; on Windows it needs Process Security Environment 1.1 host-loopback support or MXC's PSEC 1.0-only proxy-loopback compatibility capability, and a policy that uses it must also set `network.allowLocalNetwork`; compatibility applies only to an explicit identity-less runtime proxy, not general host-loopback access, and other policy restrictions still apply), `denied_paths` (native enforcement of `filesystem.deniedPaths`), `shell` (shell commands inside the sandbox), and `filesystem_enumeration` (enumerate-only filesystem grants; on Windows this needs Process Security Environment 1.1 filesystem enumeration support, and without it sandboxed PowerShell still runs but cannot resolve its current location; other platforms always report it). - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Top-level keys a repository may override through a Claude-compatible repository config. + [JsonPropertyName("claudeRepo")] + public IList ClaudeRepo { get => field ??= []; set; } - /// Human-readable reason and remedy when the feature is unsupported, such as a package to install or an OS update. Present only when `supported` is false. - [JsonPropertyName("reason")] - public string? Reason { get; set; } + /// Top-level keys an enterprise policy may fix through managed settings. + [JsonPropertyName("managed")] + public IList Managed { get => field ??= []; set; } - /// Whether this host can run the feature. - [JsonPropertyName("supported")] - public bool Supported { get; set; } + /// Top-level keys a repository may override through `.github/copilot/settings.json` or `.github/copilot/settings.local.json`. + [JsonPropertyName("repo")] + public IList Repo { get => field ??= []; set; } } -/// Whether the host running this runtime can run the command sandbox. The runtime checks `supported` once per process. A capability answer can change while the process runs, for example after the user installs a missing package. +/// Outcome of an atomic user or repository settings edit. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxHostSupport +internal sealed class SettingsEditResult { - /// Sandbox policy features whose availability varies between hosts that can run the backend. Empty when `supported` is false, because no feature can run without a backend. Later runtimes can add entries; ignore an entry whose `name` you do not recognize. - [JsonPropertyName("capabilities")] - public IList Capabilities { get => field ??= []; set; } - - /// Human-readable reason the sandbox cannot run on this host. Present only when `supported` is false. - [JsonPropertyName("reason")] - public string? Reason { get; set; } - - /// Whether a process-containment backend is usable on this host: Seatbelt on macOS, Bubblewrap on Linux, or ProcessContainer on Windows. - [JsonPropertyName("supported")] - public bool Supported { get; set; } + /// Changed, unchanged or invalid existing file. Rejected input, validation failures and I/O errors remain RPC errors. + [JsonPropertyName("status")] + public SettingsEditStatus Status { get; set; } } -/// Status of the persistent certificate authority of the sandbox credential proxy. +/// One atomic edit of a canonical setting in settings.json. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxProxyCaStatus +internal sealed class UserSettingsEditRequest { - /// Whether this process can add the certificate authority to OS trust without credentials from a different user. False where OS trust is unsupported, and on Windows when the process cannot elevate itself to write the machine trust store. When false, do not offer to set up the certificate authority. - [JsonPropertyName("canInstall")] - public bool CanInstall { get; set; } + /// Nonempty canonical path segments of the setting to edit, starting with its top-level key. + [JsonPropertyName("canonicalPath")] + public IList CanonicalPath { get => field ??= []; set; } - /// Human-readable reason for the state. On `installed` or `notInstalled`, present only when the certificate authority must be rotated, and then says why. - [JsonPropertyName("detail")] - public string? Detail { get; set; } + /// Configuration directory to edit. Omitted or empty resolves the server's own home, as in `user.settings.loadForConfigDir`. + [JsonPropertyName("configDir")] + public string? ConfigDir { get; set; } - /// The state of the certificate authority. - [JsonPropertyName("state")] - public SandboxProxyCaState State { get; set; } + /// Set a scalar, add or remove a string list item, or remove the setting. The handler rejects a missing or incompatible value. + [JsonPropertyName("operation")] + public SettingsEditOperation Operation { get; set; } + + /// Required boolean, string or number for set, required string for add-item and remove-item, and forbidden for remove. The runtime rejects all other JSON values. + [JsonPropertyName("value")] + public JsonElement? Value { get; set; } } -/// Credential-injection capability flags applied while the sandbox is enabled. For the same capability independent of sandboxing, and matched to the credential's GitHub host, see `shell.credentials`; the two are additive. +/// Settings object with canonical key names. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxConfigAuth +internal sealed class UserSettingsNormalizeResult { - /// Whether to authenticate sandboxed gh through the local masking proxy. The child receives a fake GH_TOKEN; its real value is substituted only at github.com, api.github.com and uploads.github.com (github.com because gh repo clone authenticates git through gh auth git-credential). The repository's GitHub account takes precedence over the Copilot login. Default: false (opt-in). - [JsonPropertyName("gh")] - public bool? Gh { get; set; } - - /// Whether to authenticate sandboxed HTTPS git through the local masking proxy. The child receives a fake `http.<url>.extraheader`; the real Authorization header is substituted only at its original HTTPS host, port, and repository path scope. github.com uses the Copilot token; other forges use credentials resolved from the user's own helper on the host. Default: false (opt-in). - [JsonPropertyName("git")] - public bool? Git { get; set; } + /// The same settings with every legacy or snake_case key renamed to its canonical spelling. + [JsonPropertyName("settings")] + public JsonElement Settings { get; set; } } -/// Destinations authorized to receive one masked environment credential. +/// Settings object whose key names should be canonicalized. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxMaskedEnvVar +internal sealed class UserSettingsNormalizeRequest { - /// Nonempty list of HTTPS injection hostnames or *.example.com patterns. Bare * is not accepted. These grants never override the sandbox network policy. Values in plaintext HTTP requests, URLs, bodies, encoded credentials, and signed requests are not substituted. - [JsonPropertyName("injectHosts")] - public IList InjectHosts { get => field ??= []; set; } + /// Free-form settings object, possibly using legacy or snake_case key names. + [JsonPropertyName("settings")] + public JsonElement Settings { get; set; } } -/// Whole-value environment credential masking for sandboxed children. +/// The settings layer a problem was found in. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxCredentialsConfig +public sealed class UserSettingsProblemLocation { - /// Environment variable names and their HTTPS injection destinations. Absent variables stay absent. No real values or sentinels are stored in this map. - [JsonPropertyName("envVars")] - public IDictionary EnvVars { get => field ??= new Dictionary(); set; } -} + /// Absolute path of the file backing this layer. + [JsonPropertyName("filePath")] + public string FilePath { get; set; } = string.Empty; -/// macOS seatbelt experimental options. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxConfigUserPolicyExperimentalSeatbelt -{ - /// Whether the macOS seatbelt profile may access the keychain. - [JsonPropertyName("keychainAccess")] - public bool? KeychainAccess { get; set; } -} + /// Which layer this is: `user`, `repo`, or `local`. + [JsonPropertyName("kind")] + public string Kind { get; set; } = string.Empty; -/// Platform-specific experimental policy fields. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxConfigUserPolicyExperimental -{ - /// macOS seatbelt experimental options. - [JsonPropertyName("seatbelt")] - public SandboxConfigUserPolicyExperimentalSeatbelt? Seatbelt { get; set; } + /// The caller-supplied display label for the layer. + [JsonPropertyName("label")] + public string Label { get; set; } = string.Empty; } -/// Filesystem rules to merge into the base policy. +/// One setting that will not take effect, and why. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxConfigUserPolicyFilesystem +public sealed class UserSettingsProblem { - /// Whether to clear the policy when the session exits. - [JsonPropertyName("clearPolicyOnExit")] - public bool? ClearPolicyOnExit { get; set; } + /// Coarse grouping for the problem, for a caller that presents problems in sections. + [JsonPropertyName("category")] + public string Category { get; set; } = string.Empty; - /// Paths explicitly denied. - [JsonPropertyName("deniedPaths")] - public IList? DeniedPaths { get; set; } + /// Why the setting will not take effect. Never contains the setting's value, which may be a token or another secret. + [JsonPropertyName("explanation")] + public string Explanation { get; set; } = string.Empty; - /// Paths granted read-only access. - [JsonPropertyName("readonlyPaths")] - public IList? ReadonlyPaths { get; set; } + /// Stable identifier for this problem within the reported set, so a caller can track a selection across refreshes. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// Paths granted read/write access. - [JsonPropertyName("readwritePaths")] - public IList? ReadwritePaths { get; set; } + /// What is wrong in machine-readable form, for a caller that wants to branch rather than display. + [JsonPropertyName("kind")] + public string Kind { get; set; } = string.Empty; + + /// The offending key as written in the file, when the problem is attributable to a single key. Absent when the whole file is at fault. + [JsonPropertyName("rawKey")] + public string? RawKey { get; set; } + + /// The settings layer the problem was found in. + [JsonPropertyName("source")] + public UserSettingsProblemLocation Source { get => field ??= new(); set; } } -/// HTTP proxy configuration for sandboxed traffic. +/// The settings that will not take effect across the given layers. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxConfigUserPolicyNetworkProxy +internal sealed class UserSettingsProblemsResult { - /// Optional password for proxy authentication, combined with the URL at spawn time. The persisted value may be a literal password, a `${secret:…}` reference resolved from the OS keychain, or a `${VAR}`/`$VAR` environment reference; it is resolved just before the sandboxed process routes through the proxy. The /sandbox dialog stores a real password in the OS keychain and persists only a `${secret:…}` placeholder (never plaintext in settings.json); the field is masked in the dialog and redacted by /settings show. - [JsonPropertyName("password")] - public string? Password { get; set; } - - /// Proxy URL (e.g. http://proxy.example.com:8080). The port is optional and defaults to the scheme's standard port when omitted; an explicit port must be between 1 and 65535. Credentials must not be embedded here — a `user:pass@` authority is rejected; put them in the separate `username`/`password` fields. A credential-free http:// loopback proxy URL is routed through the localhost proxy automatically; loopback covers localhost and any *.localhost subdomain, the whole 127.0.0.0/8 range, ::1, and IPv4-mapped loopback (::ffff:127.0.0.1). An https:// URL, or one with a username/password set, is used as-is. - [JsonPropertyName("url")] - public string Url { get; set; } = string.Empty; - - /// Optional username for proxy authentication. Combined with the URL (and `password`) into `user:pass@host` when the sandboxed process routes through the proxy. - [JsonPropertyName("username")] - public string? Username { get; set; } + /// Each setting that will not take effect, and why. Empty when every layer is healthy. + [JsonPropertyName("problems")] + public IList? Problems { get; set; } } -/// Network rules to merge into the base policy. +/// One already-loaded settings layer to diagnose. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxConfigUserPolicyNetwork +public sealed class UserSettingsProblemSource { - /// Hosts allowed through the built-in sandbox proxy. A non-empty list denies unmatched hosts; an absent or empty list allows all hosts not blocked. Supports exact hostnames, IP addresses, and *.example.com for strict subdomains. Host rules do not override the outbound or local-network toggles. - [JsonPropertyName("allowedHosts")] - public IList? AllowedHosts { get; set; } + /// The layer's settings exactly as loaded, unnormalized. Empty when the file is missing or failed to parse. + [JsonPropertyName("config")] + public IDictionary? Config { get; set; } - /// Whether traffic to local/loopback addresses is allowed. - [JsonPropertyName("allowLocalNetwork")] - public bool? AllowLocalNetwork { get; set; } + /// Absolute path of the file backing this layer. + [JsonPropertyName("filePath")] + public string FilePath { get; set; } = string.Empty; - /// Whether outbound network traffic is allowed at all. - [JsonPropertyName("allowOutbound")] - public bool? AllowOutbound { get; set; } + /// True when the file itself failed to parse, so its keys could not be read at all. + [JsonPropertyName("invalid")] + public bool? Invalid { get; set; } - /// Hosts denied by the built-in sandbox proxy. Deny rules take precedence over allowedHosts. A domain also denies all its subdomains. IP addresses match exactly; *.example.com matches strict subdomains, and * denies every host. - [JsonPropertyName("blockedHosts")] - public IList? BlockedHosts { get; set; } + /// Which layer this is: `user`, `repo`, or `local`. + [JsonPropertyName("kind")] + public string Kind { get; set; } = string.Empty; - /// HTTP(S) proxy for sandboxed traffic. This is the built-in local proxy's upstream: every sandboxed command reaches it through a loopback listener, so credentials stay in the runtime and never reach the child. On Windows the sandbox also needs local network access, because it reaches that listener over host loopback. Configure credentials in the separate username/password fields. The transient local listener URL is never persisted. - [JsonPropertyName("proxy")] - public SandboxConfigUserPolicyNetworkProxy? Proxy { get; set; } + /// Caller-chosen display label for the layer, echoed back on each problem so a caller can group by layer without re-deriving its own naming. + [JsonPropertyName("label")] + public string Label { get; set; } = string.Empty; } -/// macOS seatbelt-specific options. +/// The settings layers to diagnose. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxConfigUserPolicySeatbelt +internal sealed class UserSettingsProblemsRequest { - /// Whether the macOS seatbelt profile may access the keychain. - [JsonPropertyName("keychainAccess")] - public bool? KeychainAccess { get; set; } + /// Layers to diagnose, in the precedence order the caller applies them. + [JsonPropertyName("sources")] + public IList? Sources { get; set; } } -/// User-managed sandbox policy fragment merged into the auto-discovered base policy. +/// One problem found while validating a setting value. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxConfigUserPolicy +public sealed class UserSettingsValidationIssue { - /// Deprecated legacy location for `seatbelt`; read only when the top-level `seatbelt` is absent. - [JsonPropertyName("experimental")] - public SandboxConfigUserPolicyExperimental? Experimental { get; set; } - - /// Filesystem rules to merge into the base policy. - [JsonPropertyName("filesystem")] - public SandboxConfigUserPolicyFilesystem? Filesystem { get; set; } - - /// Network rules to merge into the base policy. - [JsonPropertyName("network")] - public SandboxConfigUserPolicyNetwork? Network { get; set; } + /// Human-readable description of the problem. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; - /// macOS seatbelt options to merge into the base policy. - [JsonPropertyName("seatbelt")] - public SandboxConfigUserPolicySeatbelt? Seatbelt { get; set; } + /// Where in the settings tree the problem sits, as object keys and array indices. The first element is the top-level key; a whole-value problem is reported as that single key. + [JsonPropertyName("path")] + public IList Path { get => field ??= []; set; } } -/// Resolved sandbox configuration. +/// Outcome of validating and writing one top-level setting subtree. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxConfig +internal sealed class UserSettingsApplyTopKeyResult { - /// Whether to auto-add the current working directory to readwritePaths. Default: true. - [JsonPropertyName("addCurrentWorkingDirectory")] - public bool? AddCurrentWorkingDirectory { get; set; } - - /// Whether the agent may request that an individual command run outside the sandbox, which the host then approves or denies through the usual permission flow. A host capability flag rather than part of the policy: it is stripped from the effective spawn policy and only has an effect while `enabled` is true. Fail-closed, unlike the opt-out flags on this object: omitting it offers no bypass. Default: false (opt-in). - [JsonPropertyName("allowBypass")] - public bool? AllowBypass { get; set; } - - /// Whether to auto-grant read access to tool directories discovered on PATH and in toolchain environment variables (GOROOT, JAVA_HOME, VIRTUAL_ENV, and similar), and to common developer-tool caches, config, and toolchains. Writable grants cover scratch caches, the Unix GitHub CLI cache, and Cargo's registry, git store, and lock/tracker files. A relocated CARGO_HOME gets the same narrow split: registry and git are read-write; bin is read-only; the home root, config.toml, and credentials.toml stay ungranted. Set to false to disable every grant listed above; user-installed toolchains and caches then need explicit userPolicy.filesystem readonlyPaths and readwritePaths entries. The working directory (see addCurrentWorkingDirectory), temporary storage, session log paths, and system locations follow their own rules and stay granted. Default: true (enabled by default; set to false to opt out). - [JsonPropertyName("allowDevToolAccess")] - public bool? AllowDevToolAccess { get; set; } - - /// Credential-injection capability flags. - [JsonPropertyName("auth")] - public SandboxConfigAuth? Auth { get; set; } + /// Why the write failed, present only when `status` is `write-error`. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Opt-in whole-value environment masking for sandboxed shell, MCP, and LSP children. Configured names get random sentinels; the local proxy substitutes them only in HTTPS request headers at their injection hosts. Approved bypasses skip masking and the sandbox proxy, so bypassed shells may receive the real environment values. Disabled or explicitly opted-out routes are not protected. No credential values are stored in this configuration. - [JsonPropertyName("credentials")] - public SandboxCredentialsConfig? Credentials { get; set; } + /// Problems found, present only when `status` is `validation-error`. + [JsonPropertyName("issues")] + public IList? Issues { get; set; } - /// Whether sandboxing is enabled for the session. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } + /// `ok` when the value was written, `validation-error` when it was rejected before any write, `write-error` when validation passed but the write failed. + [JsonPropertyName("status")] + public string Status { get; set; } = string.Empty; +} - /// The `sandboxLspServers` counterpart of `managedMcpRoutingLocked`. - [JsonInclude] - [JsonPropertyName("managedLspRoutingLocked")] - internal bool? ManagedLspRoutingLocked { get; set; } +/// A single top-level setting subtree to validate and write. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class UserSettingsApplyTopKeyRequest +{ + /// Canonical path of the setting the caller is editing. Currently informational only. + [JsonPropertyName("canonicalPath")] + public IList CanonicalPath { get => field ??= []; set; } - /// Set by the runtime when a managed policy forced `sandboxMcpServers` on and took the local opt-out away. Provenance rather than policy: it lets a sandbox startup failure point at the administrator instead of a setting the next managed merge would override, and it is ignored when comparing two configs for change. Only the managed merge may set it; a caller-supplied value is stripped. - [JsonInclude] - [JsonPropertyName("managedMcpRoutingLocked")] - internal bool? ManagedMcpRoutingLocked { get; set; } + /// Records that the user chose an update channel explicitly, so the one-time staff channel migration stops reapplying. Honored only for the `autoUpdatesChannel` key. + [JsonPropertyName("completeStaffUpdateChannelMigration")] + public bool? CompleteStaffUpdateChannelMigration { get; set; } - /// Whether language servers the session launches are confined by the sandbox. Only an explicit `false` opts out. Ignored while `enabled` is false. Default: true (enabled by default; set to false to opt out). - [JsonPropertyName("sandboxLspServers")] - public bool? SandboxLspServers { get; set; } + /// True to remove the top-level key instead of writing it. + [JsonPropertyName("delete")] + public bool? Delete { get; set; } - /// Whether MCP servers the session launches are confined by the sandbox. Only an explicit `false` opts out; doing so also lets remote-MCP egress leave the sandbox, so the flag and `enabled` are always read together. Ignored while `enabled` is false. Default: true (enabled by default; set to false to opt out). - [JsonPropertyName("sandboxMcpServers")] - public bool? SandboxMcpServers { get; set; } + /// Top-level setting name to write. + [JsonPropertyName("topKey")] + public string TopKey { get; set; } = string.Empty; - /// User-managed sandbox policy fragment merged into the auto-discovered base policy. - [JsonPropertyName("userPolicy")] - public SandboxConfigUserPolicy? UserPolicy { get; set; } + /// Value to write for the whole top-level key. Omitted when `delete` is true. Explicit null is preserved and validated as null. + [JsonPropertyName("value")] + public JsonElement? Value { get; set; } } -/// Identifies the credential hosts that the persistent certificate authority of the sandbox credential proxy must cover. The runtime always adds the hosts from the saved user settings. +/// The whole content to store in the user settings file. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SandboxProxyCaRequest +internal sealed class UserSettingsWriteAllRequest { - /// The sandbox configuration that the host gives its sessions. The runtime reads the credential hosts from `auth` and `credentials`; it ignores `enabled` and the other fields. - [JsonPropertyName("sandboxConfig")] - public SandboxConfig? SandboxConfig { get; set; } + /// The settings object to store. It replaces the file's entire content, so a key absent here is removed. + [JsonPropertyName("settings")] + public JsonElement Settings { get; set; } } -/// Result of creating the persistent certificate authority of the sandbox credential proxy. +/// The stored value of the user setting after the merge. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxProxyCaCreateResult +internal sealed class UserSettingsUpdateResult { - /// Absolute path of the public certificate of the certificate authority, in PEM format. - [JsonPropertyName("certificatePath")] - public string CertificatePath { get; set; } = string.Empty; + /// The key's stored value after the write: the merged object when both the stored value and the supplied value are objects, otherwise the supplied value. + [JsonPropertyName("value")] + public JsonElement Value { get; set; } } -/// Built-in tool metadata with identifier, optional namespaced name, description, input-parameter schema, and usage instructions. +/// Names the user setting to merge into, and the partial object to merge. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class Tool +internal sealed class UserSettingsUpdateRequest { - /// Description of what the tool does. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; - - /// Optional instructions for how to use this tool effectively. - [JsonPropertyName("instructions")] - public string? Instructions { get; set; } - - /// Tool identifier (e.g., "bash", "grep", "str_replace_editor"). - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Copilot configuration directory to write the settings document in, taking precedence over the server's own `COPILOT_HOME` and default home. Omit it, or pass an empty string, to use the directory the server resolved for itself. + [JsonPropertyName("configDir")] + public string? ConfigDir { get; set; } - /// Optional namespaced name for declarative filtering (e.g., "playwright/navigate" for MCP tools). - [JsonPropertyName("namespacedName")] - public string? NamespacedName { get; set; } + /// Top-level user-setting key to merge into, such as `extensions`. + [JsonPropertyName("key")] + public string Key { get; set; } = string.Empty; - /// JSON Schema for the tool's input parameters. - [JsonPropertyName("parameters")] - public IDictionary? Parameters { get; set; } + /// Single-component name for the settings document at `<configDir>/settings/<subDir>.json`; path separators, `.` and `..` are rejected. Omit it, or pass an empty string, to use the main settings document. + [JsonPropertyName("subDir")] + public string? SubDir { get; set; } - /// Telemetry-safety policy for the tool name and input names, not input values. Treat omitted metadata as unsafe. - [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] - [JsonPropertyName("safeForTelemetry")] - public JsonElement? SafeForTelemetry { get; set; } + /// Value to store for `key`. When both the stored value and this value are objects, their fields are shallow-merged and fields this object does not name are preserved. Otherwise this value replaces the stored value. + [JsonPropertyName("value")] + public JsonElement Value { get; set; } } -/// Built-in tools available for the requested model, with their parameters and instructions. +/// Contents of settings.json as stored, for a caller about to rewrite it. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ToolList +internal sealed class UserSettingsLoadForEditResult { - /// List of available built-in tools with metadata. - [JsonPropertyName("tools")] - public IList Tools { get => field ??= []; set; } + /// Parsed contents, present only when `status` is `ok`. + [JsonPropertyName("settings")] + public JsonElement? Settings { get; set; } + + /// `ok` when the file parsed, `missing` when there is no file yet, `invalid` when the file exists but will not parse. + [JsonPropertyName("status")] + public string Status { get; set; } = string.Empty; } -/// Optional model identifier whose tool overrides should be applied to the listing. +/// Outcome of validating one top-level setting subtree. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ToolsListRequest +internal sealed class UserSettingsValidateResult { - /// Optional model ID — when provided, the returned tool list reflects model-specific overrides. - [JsonPropertyName("model")] - public string? Model { get; set; } + /// Problems found, empty when `ok` is true. + [JsonPropertyName("issues")] + public IList? Issues { get; set; } + + /// True when the subtree is valid. + [JsonPropertyName("ok")] + public bool Ok { get; set; } } -/// Quota usage snapshot for a Copilot quota type, including entitlement, used requests, overage, reset date, and remaining percentage. +/// A single top-level setting subtree to validate. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AccountQuotaSnapshot +internal sealed class UserSettingsValidateRequest { - /// Number of requests included in the entitlement, or -1 for unlimited entitlements. - [JsonPropertyName("entitlementRequests")] - public long EntitlementRequests { get; set; } + /// True to validate a removal of this key. The current settings file is not read. + [JsonPropertyName("delete")] + public bool? Delete { get; set; } - /// Whether the user has an unlimited usage entitlement. - [JsonPropertyName("isUnlimitedEntitlement")] - public bool IsUnlimitedEntitlement { get; set; } - - /// Number of additional usage requests made this period. - [JsonPropertyName("overage")] - public double Overage { get; set; } + /// Top-level setting name to validate. + [JsonPropertyName("topKey")] + public string TopKey { get; set; } = string.Empty; - /// Whether additional usage is allowed when quota is exhausted. - [JsonPropertyName("overageAllowedWithExhaustedQuota")] - public bool OverageAllowedWithExhaustedQuota { get; set; } + /// Value to validate. Omitted when `delete` is true. + [JsonPropertyName("value")] + public JsonElement? Value { get; set; } +} - /// Percentage of entitlement remaining. - [JsonPropertyName("remainingPercentage")] - public double RemainingPercentage { get; set; } +/// One atomic edit of an allowed top-level repository setting. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class RepoSettingsEditRequest +{ + /// One canonical top-level repository setting that the allowlist permits. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("key")] + public string Key { get; set; } = string.Empty; - /// Date when the quota resets (ISO 8601 string). - [JsonPropertyName("resetDate")] - public DateTimeOffset? ResetDate { get; set; } + /// Set a scalar, add or remove a string list item, or remove the setting. The handler rejects a missing or incompatible value. + [JsonPropertyName("operation")] + public SettingsEditOperation Operation { get; set; } - /// Whether usage is still permitted after quota exhaustion. - [JsonPropertyName("usageAllowedWithExhaustedQuota")] - public bool UsageAllowedWithExhaustedQuota { get; set; } + /// Absolute path of the repository root. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("repoRoot")] + public string RepoRoot { get; set; } = string.Empty; - /// Number of requests used so far this period. - [JsonPropertyName("usedRequests")] - public long UsedRequests { get; set; } -} + /// Shared or local repository settings file. + [JsonPropertyName("scope")] + public RepoSettingsEditScope Scope { get; set; } -/// Quota usage snapshots for the resolved user, keyed by quota type. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AccountGetQuotaResult -{ - /// Quota snapshots keyed by type (e.g., chat, completions, premium_interactions). - [JsonPropertyName("quotaSnapshots")] - public IDictionary QuotaSnapshots { get => field ??= new Dictionary(); set; } + /// Required boolean, string or number for set, required string for add-item and remove-item, and forbidden for remove. The runtime rejects all other JSON values. + [JsonPropertyName("value")] + public JsonElement? Value { get; set; } } -/// RPC data type for AccountGetQuota operations. +/// Parsed repository settings without normalization or layering. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AccountGetQuotaRequest +internal sealed class RepoSettingsReadRawResult { - /// GitHub token accepted for compatibility with existing SDK clients. When provided, resolves this token instead of using the current account. - [JsonPropertyName("gitHubToken")] - public string? GitHubToken { get; set; } + /// Stored JSON values after the existing JSONC parser and prototype-safety checks. Empty when the file is missing, blank, or invalid. + [JsonPropertyName("config")] + public JsonElement Config { get; set; } - /// Opaque account identifier returned by `account.getAllUsers`. When omitted, the current account is used. - [JsonPropertyName("selectionId")] - public string? SelectionId { get; set; } + /// True when the file exists but did not parse, so `config` is empty for that reason rather than because nothing is set. + [JsonPropertyName("invalid")] + public bool Invalid { get; set; } } -/// Authentication credentials accepted only at native protocol ingress. Runtime outputs use credential-free `AuthIdentity` metadata. -/// Polymorphic base type discriminated by type. +/// Which repository settings file to read. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "type", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(AuthInfoHmac), "hmac")] -[JsonDerivedType(typeof(AuthInfoEnv), "env")] -[JsonDerivedType(typeof(AuthInfoToken), "token")] -[JsonDerivedType(typeof(AuthInfoTokenProvider), "token-provider")] -[JsonDerivedType(typeof(AuthInfoCopilotApiToken), "copilot-api-token")] -[JsonDerivedType(typeof(AuthInfoUser), "user")] -[JsonDerivedType(typeof(AuthInfoAccount), "account")] -[JsonDerivedType(typeof(AuthInfoGhCli), "gh-cli")] -[JsonDerivedType(typeof(AuthInfoApiKey), "api-key")] -public partial class AuthInfo +internal sealed class RepoSettingsReadRawRequest { - /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { get; set; } = string.Empty; -} + /// Absolute path of the repository root. + [JsonPropertyName("repoRoot")] + public string RepoRoot { get; set; } = string.Empty; + /// Use repo for shared settings or local for settings.local.json. Other scopes are rejected. + [JsonPropertyName("scope")] + public string Scope { get; set; } = string.Empty; +} -/// Endpoint URLs from the raw Copilot `/copilot_internal/v2/token` user-response passthrough. +/// One key to write or remove in a repository settings file. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CopilotUserResponseEndpoints +internal sealed class RepoSettingsWriteKeyRequest { - /// Copilot API endpoint URL. - [JsonPropertyName("api")] - public string? Api { get; set; } - - /// Experimental-service endpoint URL. - [JsonPropertyName("exp")] - public string? Exp { get; set; } + /// True to remove the key instead of writing it. + [JsonPropertyName("delete")] + public bool? Delete { get; set; } - /// Origin-tracker endpoint URL. - [JsonPropertyName("origin-tracker")] - public string? OriginTracker { get; set; } + /// Canonical top-level key permitted by the repository settings allowlist. + [JsonPropertyName("key")] + public string Key { get; set; } = string.Empty; - /// Copilot proxy endpoint URL. - [JsonPropertyName("proxy")] - public string? Proxy { get; set; } + /// Absolute path of the repository root. + [JsonPropertyName("repoRoot")] + public string RepoRoot { get; set; } = string.Empty; - /// Copilot telemetry endpoint URL. - [JsonPropertyName("telemetry")] - public string? Telemetry { get; set; } -} + /// Use repo for shared settings or local for settings.local.json. Other scopes are rejected. + [JsonPropertyName("scope")] + public string Scope { get; set; } = string.Empty; -/// RPC data type for CopilotUserResponseEnterpriseListItem operations. -public sealed class CopilotUserResponseEnterpriseListItem -{ - /// JavaScript-safe numeric database ID of the enterprise. - [JsonPropertyName("id")] - public long? Id { get; set; } + /// Value to validate and write. Required unless delete is true; a null value is validated, not treated as deletion. + [JsonPropertyName("value")] + public JsonElement? Value { get; set; } } -/// RPC data type for CopilotUserResponseOrganizationListItem operations. -public sealed class CopilotUserResponseOrganizationListItem +/// The complete set of repository-overridable settings to store. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class RepoSettingsWriteAllRequest { - /// Numeric database ID of the organization. - [JsonPropertyName("id")] - public double? Id { get; set; } + /// Absolute path of the repository root. + [JsonPropertyName("repoRoot")] + public string RepoRoot { get; set; } = string.Empty; - /// GitHub login of the organization. - [JsonPropertyName("login")] - public string? Login { get; set; } + /// Use repo for shared settings or local for settings.local.json. Other scopes are rejected. + [JsonPropertyName("scope")] + public string Scope { get; set; } = string.Empty; - /// Display name of the organization. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// Object of allowed settings to store. Missing allowlisted keys are removed. Other input keys are ignored, while unknown stored keys are preserved. + [JsonPropertyName("settings")] + public JsonElement Settings { get; set; } } -/// Chat quota snapshot from the raw Copilot user-response passthrough, with entitlement, overage, remaining quota, reset, and billing fields. +/// Caller population and startup inputs to resolve named online-experiment gates for. Carries no account: the runtime resolves the assignment for the authentication it already holds. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CopilotUserResponseQuotaSnapshotsChat +internal sealed class FeatureFlagsResolveStartupGatesRequest { - /// Number of requests/units included in the entitlement for this period; `-1` denotes an unlimited entitlement. - [JsonPropertyName("entitlement")] - public double? Entitlement { get; set; } - - /// Whether the user currently has quota available; when `false` and not unlimited, further requests are blocked until the quota resets. - [JsonPropertyName("has_quota")] - public bool? HasQuota { get; set; } + /// User settings document whose `featureFlags` block is layered on top of the resolved set. + [JsonPropertyName("config")] + public JsonElement? Config { get; set; } - /// Count of additional pay-per-request usage consumed this period beyond the entitlement. - [JsonPropertyName("overage_count")] - public double? OverageCount { get; set; } + /// Environment to read flag overrides from. Defaults to this runtime process's own environment. + [JsonPropertyName("environment")] + public IDictionary? Environment { get; set; } - /// Whether usage may continue at pay-per-request rates once the entitlement is exhausted. - [JsonPropertyName("overage_permitted")] - public bool? OveragePermitted { get; set; } + /// When this installation first launched, in milliseconds since the Unix epoch. Experiments that segment on installation age read it. Omit when the first launch was never recorded. + [JsonPropertyName("firstLaunchAtMillis")] + public double? FirstLaunchAtMillis { get; set; } - /// Percentage of the entitlement remaining at the snapshot timestamp. - [JsonPropertyName("percent_remaining")] - public double? PercentRemaining { get; set; } + /// The feature flags to resolve. A name this runtime does not declare is reported as an error rather than as a disabled flag. + [JsonPropertyName("flags")] + public IList Flags { get => field ??= []; set; } - /// Identifier of the quota bucket this snapshot describes. - [JsonPropertyName("quota_id")] - public string? QuotaId { get; set; } + /// Whether the caller is in an experimental population. + [JsonPropertyName("isExperimental")] + public bool IsExperimental { get; set; } - /// Amount of quota remaining at the snapshot timestamp. - [JsonPropertyName("quota_remaining")] - public double? QuotaRemaining { get; set; } + /// Whether the caller is GitHub or Microsoft staff. + [JsonPropertyName("isStaff")] + public bool IsStaff { get; set; } - /// Unix epoch time, in seconds, when this quota next resets. - [JsonPropertyName("quota_reset_at")] - public double? QuotaResetAt { get; set; } + /// GitHub-staff membership. Omit when it was never recorded: a legacy staff account then counts as both staff populations and keeps every staff feature. + [JsonPropertyName("isStaffGithub")] + public bool? IsStaffGitHub { get; set; } - /// Remaining entitlement/quota amount at the snapshot timestamp. - [JsonPropertyName("remaining")] - public double? Remaining { get; set; } + /// Microsoft-staff membership. Omitted the same way as `isStaffGitHub`. + [JsonPropertyName("isStaffMicrosoft")] + public bool? IsStaffMicrosoft { get; set; } - /// UTC timestamp when this snapshot was captured. - [JsonPropertyName("timestamp_utc")] - public string? TimestampUtc { get; set; } + /// Whether the caller belongs to a team plan. + [JsonPropertyName("isTeam")] + public bool IsTeam { get; set; } - /// Whether this category uses usage-based (token/AI-credit) billing rather than a fixed premium-request count. - [JsonPropertyName("token_based_billing")] - public bool? TokenBasedBilling { get; set; } + /// Whether the runtime may refresh the cached assignment over the network without an active session. The refresh never blocks this call: the answer comes from the cache either way, so a refresh only affects a later launch. + [JsonPropertyName("refreshWithoutSession")] + public bool RefreshWithoutSession { get; set; } - /// Whether the entitlement for this category is unlimited. - [JsonPropertyName("unlimited")] - public bool? Unlimited { get; set; } + /// Whether streamer mode is on, which withholds the populations an experiment would otherwise segment on. + [JsonPropertyName("streamerMode")] + public bool StreamerMode { get; set; } } -/// Completions quota snapshot from the raw Copilot user-response passthrough, with entitlement, overage, remaining quota, reset, and billing fields. +/// The declared shape of one feature flag, independent of any caller population. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CopilotUserResponseQuotaSnapshotsCompletions +internal sealed class FeatureFlagMetadata { - /// Number of requests/units included in the entitlement for this period; `-1` denotes an unlimited entitlement. - [JsonPropertyName("entitlement")] - public double? Entitlement { get; set; } - - /// Whether the user currently has quota available; when `false` and not unlimited, further requests are blocked until the quota resets. - [JsonPropertyName("has_quota")] - public bool? HasQuota { get; set; } - - /// Count of additional pay-per-request usage consumed this period beyond the entitlement. - [JsonPropertyName("overage_count")] - public double? OverageCount { get; set; } - - /// Whether usage may continue at pay-per-request rates once the entitlement is exhausted. - [JsonPropertyName("overage_permitted")] - public bool? OveragePermitted { get; set; } - - /// Percentage of the entitlement remaining at the snapshot timestamp. - [JsonPropertyName("percent_remaining")] - public double? PercentRemaining { get; set; } - - /// Identifier of the quota bucket this snapshot describes. - [JsonPropertyName("quota_id")] - public string? QuotaId { get; set; } + /// The population the flag is declared for before any environment or user-settings override is applied. + [JsonPropertyName("availability")] + public FeatureFlagAvailability Availability { get; set; } - /// Amount of quota remaining at the snapshot timestamp. - [JsonPropertyName("quota_remaining")] - public double? QuotaRemaining { get; set; } + /// Whether the flag participates in the CAPI sanity suite. + [JsonPropertyName("capiSanity")] + public bool CapiSanity { get; set; } - /// Unix epoch time, in seconds, when this quota next resets. - [JsonPropertyName("quota_reset_at")] - public double? QuotaResetAt { get; set; } + /// Text shown to a user choosing whether to turn the flag on in experimental mode. Absent when the flag is not user-facing. + [JsonPropertyName("experimentalDescription")] + public string? ExperimentalDescription { get; set; } - /// Remaining entitlement/quota amount at the snapshot timestamp. - [JsonPropertyName("remaining")] - public double? Remaining { get; set; } + /// The online-experiment (ExP) flag this maps to, when the flag is assigned by an experiment rather than declared statically. + [JsonPropertyName("expFlag")] + public string? ExpFlag { get; set; } - /// UTC timestamp when this snapshot was captured. - [JsonPropertyName("timestamp_utc")] - public string? TimestampUtc { get; set; } + /// Environment variable that overrides this flag's experiment assignment locally in the experiment service, not in `featureFlags.resolve`. Present exactly when `expFlag` is, and derived from it, so a caller reads a local override without reproducing the naming rule. + [JsonPropertyName("expFlagEnvVar")] + public string? ExpFlagEnvVar { get; set; } - /// Whether this category uses usage-based (token/AI-credit) billing rather than a fixed premium-request count. - [JsonPropertyName("token_based_billing")] - public bool? TokenBasedBilling { get; set; } + /// The flag's name, matching the key it is listed under. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; +} - /// Whether the entitlement for this category is unlimited. - [JsonPropertyName("unlimited")] - public bool? Unlimited { get; set; } +/// Every feature flag this runtime declares, with the shape each one was declared with. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class FeatureFlagsMetadataResult +{ + /// Declared feature flags keyed by flag name. + [JsonPropertyName("flags")] + public IDictionary Flags { get => field ??= new Dictionary(); set; } } -/// Premium-interactions quota snapshot from the raw Copilot user-response passthrough, with entitlement, overage, remaining quota, reset, and billing fields. +/// Caller population and overrides for static feature-flag resolution, independent of experiment assignments and active runtime state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CopilotUserResponseQuotaSnapshotsPremiumInteractions +internal sealed class FeatureFlagsResolveRequest { - /// Number of requests/units included in the entitlement for this period; `-1` denotes an unlimited entitlement. - [JsonPropertyName("entitlement")] - public double? Entitlement { get; set; } + /// Resolve against this already-computed flag set rather than each flag's declared availability. Keyed by flag name. + [JsonPropertyName("baseFlags")] + public IDictionary? BaseFlags { get; set; } - /// Whether the user currently has quota available; when `false` and not unlimited, further requests are blocked until the quota resets. - [JsonPropertyName("has_quota")] - public bool? HasQuota { get; set; } + /// User settings to apply, including `enabledFeatureFlags`, legacy `feature_flags.enabled`, and supported convenience settings. + [JsonPropertyName("config")] + public JsonElement? Config { get; set; } - /// Count of additional pay-per-request usage consumed this period beyond the entitlement. - [JsonPropertyName("overage_count")] - public double? OverageCount { get; set; } + /// Environment for direct feature-flag overrides and `COPILOT_CLI_ENABLED_FEATURE_FLAGS`. Does not apply `COPILOT_EXP_*` experiment assignments. Defaults to this runtime process's own environment. + [JsonPropertyName("environment")] + public IDictionary? Environment { get; set; } - /// Whether usage may continue at pay-per-request rates once the entitlement is exhausted. - [JsonPropertyName("overage_permitted")] - public bool? OveragePermitted { get; set; } + /// Flags forced to a value after every other layer, keyed by flag name. + [JsonPropertyName("flagOverrides")] + public IDictionary? FlagOverrides { get; set; } - /// Percentage of the entitlement remaining at the snapshot timestamp. - [JsonPropertyName("percent_remaining")] - public double? PercentRemaining { get; set; } + /// Whether the caller is in an experimental population. + [JsonPropertyName("isExperimental")] + public bool IsExperimental { get; set; } - /// Identifier of the quota bucket this snapshot describes. - [JsonPropertyName("quota_id")] - public string? QuotaId { get; set; } + /// Whether the caller is GitHub or Microsoft staff. + [JsonPropertyName("isStaff")] + public bool IsStaff { get; set; } - /// Amount of quota remaining at the snapshot timestamp. - [JsonPropertyName("quota_remaining")] - public double? QuotaRemaining { get; set; } + /// GitHub-staff membership. Omit when it was never recorded: a legacy staff account then counts as both staff populations and keeps every staff feature. + [JsonPropertyName("isStaffGithub")] + public bool? IsStaffGitHub { get; set; } - /// Unix epoch time, in seconds, when this quota next resets. - [JsonPropertyName("quota_reset_at")] - public double? QuotaResetAt { get; set; } + /// Microsoft-staff membership. Omitted the same way as `isStaffGitHub`. + [JsonPropertyName("isStaffMicrosoft")] + public bool? IsStaffMicrosoft { get; set; } - /// Remaining entitlement/quota amount at the snapshot timestamp. - [JsonPropertyName("remaining")] - public double? Remaining { get; set; } + /// Whether the caller belongs to the team population used by feature availability. + [JsonPropertyName("isTeam")] + public bool IsTeam { get; set; } +} - /// UTC timestamp when this snapshot was captured. - [JsonPropertyName("timestamp_utc")] - public string? TimestampUtc { get; set; } +/// The policy questions a host asks before granting elevated permissions. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class PermissionsEvaluateManagedPolicyResult +{ + /// True when managed policy forbids bypass-permissions mode, so allow-all style flags must not take effect. + [JsonPropertyName("bypassPermissionsDisabled")] + public bool BypassPermissionsDisabled { get; set; } - /// Whether this category uses usage-based (token/AI-credit) billing rather than a fixed premium-request count. - [JsonPropertyName("token_based_billing")] - public bool? TokenBasedBilling { get; set; } + /// True when the managed layer supplies permission rules that the session must enforce. + [JsonPropertyName("permissionRulesActive")] + public bool PermissionRulesActive { get; set; } - /// Whether the entitlement for this category is unlimited. - [JsonPropertyName("unlimited")] - public bool? Unlimited { get; set; } + /// True when a managed policy exists but this build could not determine its meaning. Hosts fail closed on it rather than assuming permission was granted. + [JsonPropertyName("policyUndetermined")] + public bool PolicyUndetermined { get; set; } } -/// Quota snapshot map from the raw Copilot user-response passthrough, with chat, completions, premium-interactions, and other entries. +/// The verbatim managed `permissions` slice to interpret. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CopilotUserResponseQuotaSnapshots +internal sealed class PermissionsEvaluateManagedPolicyParams { - /// Chat quota snapshot from the raw Copilot user-response passthrough, with entitlement, overage, remaining quota, reset, and billing fields. - [JsonPropertyName("chat")] - public CopilotUserResponseQuotaSnapshotsChat? Chat { get; set; } + /// The `permissions` object from a managed settings layer, for example `{"disableBypassPermissionsMode":"disable"}`. Omitted or null means no managed policy applies. + [JsonPropertyName("permissions")] + public JsonElement? Permissions { get; set; } +} - /// Completions quota snapshot from the raw Copilot user-response passthrough, with entitlement, overage, remaining quota, reset, and billing fields. - [JsonPropertyName("completions")] - public CopilotUserResponseQuotaSnapshotsCompletions? Completions { get; set; } +/// Which stored location permissions were dropped because their directory is gone. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class LocationPermissionsCleanupResult +{ + /// Location keys whose grants were removed. Empty when every stored location still exists. + [JsonPropertyName("removedLocationKeys")] + public IList RemovedLocationKeys { get => field ??= []; set; } +} - /// Premium-interactions quota snapshot from the raw Copilot user-response passthrough, with entitlement, overage, remaining quota, reset, and billing fields. - [JsonPropertyName("premium_interactions")] - public CopilotUserResponseQuotaSnapshotsPremiumInteractions? PremiumInteractions { get; set; } +/// Folder trust check result. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class FolderTrustCheckResult +{ + /// Whether the folder is trusted. + [JsonPropertyName("trusted")] + public bool Trusted { get; set; } } -/// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. +/// A folder to check against a persisted trusted-folders list, and the configuration directory holding that list. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CopilotUserResponse +internal sealed class FolderTrustIsTrustedParams { - /// Copilot access SKU identifier (e.g. `free_limited_copilot`, `copilot_for_business_seat_quota`) used to gate model and feature access. - [JsonPropertyName("access_type_sku")] - public string? AccessTypeSku { get; set; } + /// Configuration directory to read the trusted-folders list from. Omit it to read the default location; supply it when the host has redirected its configuration, or the check answers from a list the host is not using. + [JsonPropertyName("configDir")] + public string? ConfigDir { get; set; } - /// Opaque analytics tracking identifier for the user, forwarded from the Copilot API. - [JsonPropertyName("analytics_tracking_id")] - public string? AnalyticsTrackingId { get; set; } - - /// Date the Copilot seat was assigned to the user, if applicable. - [JsonPropertyName("assigned_date")] - public string? AssignedDate { get; set; } - - /// Whether the user is eligible to sign up for the free/limited Copilot tier. - [JsonPropertyName("can_signup_for_limited")] - public bool? CanSignupForLimited { get; set; } - - /// Whether the user is able to upgrade their Copilot plan. - [JsonPropertyName("can_upgrade_plan")] - public bool? CanUpgradePlan { get; set; } - - /// Whether Copilot chat is enabled for the user. - [JsonPropertyName("chat_enabled")] - public bool? ChatEnabled { get; set; } - - /// Whether CLI remote control is enabled for the user. - [JsonPropertyName("cli_remote_control_enabled")] - public bool? CliRemoteControlEnabled { get; set; } - - /// Whether cloud session storage is enabled for the user. - [JsonPropertyName("cloud_session_storage_enabled")] - public bool? CloudSessionStorageEnabled { get; set; } + /// Folder to check. A folder counts as trusted when it is itself listed, when an ancestor is listed, or when its git root is listed. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; +} - /// Whether the Codex agent is enabled for the user. - [JsonPropertyName("codex_agent_enabled")] - public bool? CodexAgentEnabled { get; set; } +/// The device-managed settings layer, with the sandbox floor and the load outcome a host needs to compose it into an effective policy. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ManagedSettingsReadDeviceResult +{ + /// Whether discovery or validation failed. A host must treat this as "policy unknown" rather than "no policy": absent settings with this set false mean the device genuinely has none. + [JsonPropertyName("loadFailed")] + public bool LoadFailed { get; set; } - /// Copilot plan name for the user (e.g. `individual`, `business`, `enterprise`). - [JsonPropertyName("copilot_plan")] - public string? CopilotPlan { get; set; } + /// Sandbox floor read from the discovered document before validation, so a document that is otherwise rejected can still raise the floor it declared. + [JsonPropertyName("sandboxFloor")] + public JsonElement? SandboxFloor { get; set; } - /// Whether `.copilotignore` content-exclusion support is enabled for the user. - [JsonPropertyName("copilotignore_enabled")] - public bool? CopilotignoreEnabled { get; set; } + /// Validated, canonical device-managed settings. Omitted when no device policy was discovered, when the discovered document failed validation, or when it validated to an empty object — an empty document carries no policy, and reporting it as present would outrank layers that do. + [JsonPropertyName("settings")] + public JsonElement? Settings { get; set; } +} - /// Endpoint URLs from the raw Copilot `/copilot_internal/v2/token` user-response passthrough. - [JsonPropertyName("endpoints")] - public CopilotUserResponseEndpoints? Endpoints { get; set; } +/// The managed layers to retain as this process's startup policy, with the outcome of fetching each one. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ManagedSettingsRetainPolicyParams +{ + /// Whether the device load failed. Report it honestly, for the same reason as `serverFetchFailed`. + [JsonPropertyName("deviceLoadFailed")] + public bool DeviceLoadFailed { get; set; } - /// Enterprises that provide the user's Copilot license; malformed entries are normalized to null or ID-less shapes. - [JsonPropertyName("enterprise_list")] - public IList? EnterpriseList { get; set; } + /// The device/MDM policy layer. + [JsonPropertyName("deviceResponse")] + public JsonElement? DeviceResponse { get; set; } - /// Whether MCP (Model Context Protocol) support is enabled for the user. - [JsonPropertyName("is_mcp_enabled")] - public bool? IsMcpEnabled { get; set; } + /// Sandbox floor declared by the device layer, carried separately so a document that failed validation can still raise the floor. + [JsonPropertyName("deviceSandboxFloor")] + public JsonElement? DeviceSandboxFloor { get; set; } - /// Whether the user is a GitHub/Microsoft staff member. - [JsonPropertyName("is_staff")] - public bool? IsStaff { get; set; } + /// Whether the device sandbox intent is unreadable independently of its other settings. + [JsonPropertyName("deviceSandboxUndetermined")] + public bool DeviceSandboxUndetermined { get; set; } - /// Per-category quota allotments for free/limited-tier users, keyed by quota category. - [JsonPropertyName("limited_user_quotas")] - public IDictionary? LimitedUserQuotas { get; set; } + /// Whether a device policy source demonstrably exists. This, not a read error, is the evidence a device is managed, so a fail-closed policy gates on it. + [JsonPropertyName("deviceSourceFound")] + public bool DeviceSourceFound { get; set; } - /// Date the free/limited-tier user's quotas next reset, as a raw string from the Copilot API. - [JsonPropertyName("limited_user_reset_date")] - public string? LimitedUserResetDate { get; set; } + /// Whether any managed policy source exists, independently of fetch failures. + [JsonPropertyName("managedPolicyPresent")] + public bool ManagedPolicyPresent { get; set; } - /// GitHub login of the authenticated user. - [JsonPropertyName("login")] - public string? Login { get; set; } + /// Whether permission policy is undetermined. Sandbox-only uncertainty does not imply this flag. + [JsonPropertyName("permissionPolicyUndetermined")] + public bool PermissionPolicyUndetermined { get; set; } - /// Per-category monthly quota allotments, keyed by quota category. - [JsonPropertyName("monthly_quotas")] - public IDictionary? MonthlyQuotas { get; set; } + /// The policy helper's response: the third managed layer that a process owning its startup policy resolves. It is retained with the other two so that every ambient read in this process, including the mutation guards and the governed marketplace allowlist that the `plugins.*` handlers derive, sees the whole policy the process fetched. A session-owning process omits it, because it resolves a helper per session against that session's working directory. + [JsonPropertyName("policyHelperResponse")] + public JsonElement? PolicyHelperResponse { get; set; } - /// Organizations the user belongs to, each with an optional ID, login, and display name. - [JsonPropertyName("organization_list")] - public IList? OrganizationList { get; set; } + /// Preserve the retained server layer during a device-only resolution. + [JsonPropertyName("preserveServerState")] + public bool PreserveServerState { get; set; } - /// Logins of the organizations the user belongs to. - [JsonPropertyName("organization_login_list")] - public IList? OrganizationLoginList { get; set; } + /// Whether uncertainty alone enabled the shared sandbox floor. + [JsonPropertyName("sandboxEnabledByUndeterminedPolicy")] + public bool SandboxEnabledByUndeterminedPolicy { get; set; } - /// Date the user's usage quota next resets, as a raw string from the Copilot API; see `quota_reset_date_utc` for the UTC-normalized value. - [JsonPropertyName("quota_reset_date")] - public string? QuotaResetDate { get; set; } + /// Whether the shared sandbox policy is undetermined. Diagnostic errors alone do not imply this flag. + [JsonPropertyName("sandboxFloorUndetermined")] + public bool SandboxFloorUndetermined { get; set; } - /// UTC-normalized form of `quota_reset_date` (the date the user's usage quota next resets). - [JsonPropertyName("quota_reset_date_utc")] - public string? QuotaResetDateUtc { get; set; } + /// Whether the server fetch failed. Report it honestly: true keeps the already-retained server layer rather than replacing it with an absent one. + [JsonPropertyName("serverFetchFailed")] + public bool ServerFetchFailed { get; set; } - /// Quota snapshot map from the raw Copilot user-response passthrough, with chat, completions, premium-interactions, and other entries. - [JsonPropertyName("quota_snapshots")] - public CopilotUserResponseQuotaSnapshots? QuotaSnapshots { get; set; } + /// Whether a required fresh server-policy confirmation failed. + [JsonPropertyName("serverForcedRefreshFailure")] + public bool ServerForcedRefreshFailure { get; set; } - /// Whether the user's telemetry is subject to restricted-data handling. - [JsonPropertyName("restricted_telemetry")] - public bool? RestrictedTelemetry { get; set; } + /// The resolved server policy. A known source can lack a response after a forced-refresh failure. + [JsonPropertyName("serverResponse")] + public JsonElement? ServerResponse { get; set; } - /// Raw passthrough of the Copilot API `te` flag for the user (an opaque server-side eligibility signal surfaced in telemetry); not otherwise interpreted by the runtime. - [JsonPropertyName("te")] - public bool? Te { get; set; } + /// Whether the server policy source exists, even when its response is unavailable. + [JsonPropertyName("serverSourceFound")] + public bool ServerSourceFound { get; set; } - /// Whether the account is on usage-based (token/AI-credit) billing rather than a fixed premium-request quota. - [JsonPropertyName("token_based_billing")] - public bool? TokenBasedBilling { get; set; } + /// Shared server/device sandbox floor, excluding session-local helper policy. + [JsonPropertyName("sharedSandboxFloor")] + public JsonElement? SharedSandboxFloor { get; set; } } -/// Authentication-info input variant for GitHub-internal HMAC auth, carrying the public GitHub host and HMAC secret. -/// The hmac variant of . +/// The user settings with the managed layer's controlled entries stamped on. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthInfoHmac : AuthInfo +internal sealed class ManagedSettingsApplyControlsResult { - /// - [JsonIgnore] - public override string Type => "hmac"; + /// The resulting settings document. + [JsonPropertyName("settingsJson")] + public JsonElement SettingsJson { get; set; } +} - /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("copilotUser")] - public CopilotUserResponse? CopilotUser { get; set; } +/// A user settings document and the effective managed policy whose controlled entries should be stamped onto it. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ManagedSettingsApplyControlsParams +{ + /// The effective managed policy, as composed by `managedSettings.resolveEffectiveValue`. Omit it on an unmanaged host: the settings then come back unchanged. + [JsonPropertyName("managed")] + public JsonElement? Managed { get; set; } - /// HMAC secret used to sign requests. - [JsonPropertyName("hmac")] - public required string Hmac { get; set; } + /// The user settings document to stamp. Entries the managed layer does not name are returned exactly as supplied. + [JsonPropertyName("settings")] + public JsonElement Settings { get; set; } +} - /// Authentication host. HMAC auth always targets the public GitHub host. - [JsonPropertyName("host")] - public required string Host { get; set; } +/// The accepted managed settings, reduced to the keys a client is allowed to set. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ManagedSettingsParseClientValueResult +{ + /// The normalized settings. Omitted when the client supplied none. An accepted document that sets nothing comes back as an empty object, which is distinct from omission. + [JsonPropertyName("settingsJson")] + public JsonElement? SettingsJson { get; set; } } -/// Authentication-info input variant for a token sourced from an environment variable, with host, optional login, token, and env var name. -/// The env variant of . +/// The client-supplied managed settings to validate and normalize. Omitting them is valid and yields no policy. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthInfoEnv : AuthInfo +internal sealed class ManagedSettingsParseClientValueParams { - /// - [JsonIgnore] - public override string Type => "env"; + /// The managed settings a client is supplying for a session it is about to open. Omit them when the client is imposing no policy of its own. + [JsonPropertyName("settings")] + public JsonElement? Settings { get; set; } +} - /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("copilotUser")] - public CopilotUserResponse? CopilotUser { get; set; } +/// The effective managed policy composed from the supplied layers. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ManagedSettingsResolveEffectiveValueResult +{ + /// The composed effective managed settings. Omitted when no layer sets anything, so an unmanaged host locks nothing. Carries `sandboxAdvisoryFloorDeclined: true` when an advisory server force-on was dropped because this host cannot run the sandbox. + [JsonPropertyName("settingsJson")] + public JsonElement? SettingsJson { get; set; } +} - /// Name of the environment variable the token was sourced from. - [JsonPropertyName("envVar")] - public required string EnvVar { get; set; } +/// The managed-settings layers to compose into an effective policy. Every layer is optional; omitting all of them yields no effective policy. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ManagedSettingsResolveEffectiveValueParams +{ + /// The device sandbox floor, folded into the device layer under `sandbox` before composition. + [JsonPropertyName("deviceSandboxFloor")] + public JsonElement? DeviceSandboxFloor { get; set; } - /// Authentication host (e.g. https://github.com or a GHES host). - [JsonPropertyName("host")] - public required string Host { get; set; } + /// The device layer: native MDM and managed-file settings describing the machine rather than the account. + [JsonPropertyName("mdm")] + public JsonElement? Mdm { get; set; } - /// User login associated with the token. Undefined for server-to-server tokens (those starting with `ghs_`). - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("login")] - public string? Login { get; set; } + /// The policy-helper layer, an enforced channel sourced from a platform policy helper process. + [JsonPropertyName("policyHelper")] + public JsonElement? PolicyHelper { get; set; } - /// The token value itself. Treat as a secret. - [JsonPropertyName("token")] - public required string Token { get; set; } + /// The account's server-delivered org policy, as returned by a managed-settings fetch. Omitted before sign-in or when the fetch has not landed. + [JsonPropertyName("server")] + public JsonElement? Server { get; set; } } -/// Authentication-info input variant for SDK-configured token authentication, carrying host and the secret token value. -/// The token variant of . +/// The bypass-permissions state a host should hold after the resolution, and what it must do to get there. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthInfoToken : AuthInfo +internal sealed class ManagedSettingsResolveBypassDisableTransitionResult { - /// - [JsonIgnore] - public override string Type => "token"; - - /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("copilotUser")] - public CopilotUserResponse? CopilotUser { get; set; } + /// The account the returned `managed` source is pinned to. Store it alongside `source`; the next call needs it to decide whether an account switch lifts the disable. + [JsonPropertyName("managedIdentity")] + public string? ManagedIdentity { get; set; } - /// Authentication host. - [JsonPropertyName("host")] - public required string Host { get; set; } + /// The value to store in the host's own bypass-disabled flag. Omitted means leave it exactly as it is, which is not the same as being told to store `false`. + [JsonPropertyName("setDisabledState")] + public bool? SetDisabledState { get; set; } - /// Opaque native GitHub credential registration backing this token identity, when applicable. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("registrationId")] - public string? RegistrationId { get; set; } + /// Which layer disables bypass mode after this resolution. Omitted when none does, which is what a lifted disable looks like. + [JsonPropertyName("source")] + public ManagedSettingsBypassDisableSource? Source { get; set; } - /// The token value itself. Treat as a secret. - [JsonPropertyName("token")] - public required string Token { get; set; } + /// Whether a bypass-permissions session already running must be turned off. True only when this resolution newly disables bypass mode, so a host that is already compliant is not interrupted. + [JsonPropertyName("suppressRuntimeAllowAll")] + public bool SuppressRuntimeAllowAll { get; set; } } -/// Authentication-info variant backed by an SDK GitHub token callback. It carries routing metadata but never a plaintext token. -/// The token-provider variant of . +/// The bypass-permissions state a host holds today, the account now signed in, and what the managed-policy resolution said. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthInfoTokenProvider : AuthInfo +internal sealed class ManagedSettingsResolveBypassDisableTransitionParams { - /// - [JsonIgnore] - public override string Type => "token-provider"; + /// The account signed in now. A `managed` disable pinned to a different account is lifted. + [JsonPropertyName("activeIdentity")] + public string ActiveIdentity { get; set; } = string.Empty; - /// Snapshot of the authenticated user's Copilot subscription info, if known. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("copilotUser")] - public CopilotUserResponse? CopilotUser { get; set; } + /// The account a `managed` disable is pinned to. Omit it when the current source is not `managed`; it is ignored for the other sources. + [JsonPropertyName("currentManagedIdentity")] + public string? CurrentManagedIdentity { get; set; } - /// Authentication host. - [JsonPropertyName("host")] - public required string Host { get; set; } + /// Which layer currently disables bypass mode. Omit it when nothing does. + [JsonPropertyName("currentSource")] + public ManagedSettingsBypassDisableSource? CurrentSource { get; set; } - /// Opaque SDK callback registration identifier. - [JsonPropertyName("registrationId")] - public required string RegistrationId { get; set; } + /// What the resolution said about bypass mode. Send `unknown` rather than omitting the field when there is no verdict. + [JsonPropertyName("policy")] + public ManagedSettingsBypassPolicyResolution Policy { get; set; } } -/// Authentication-info variant for direct Copilot API token auth sourced from environment variables, with public GitHub host. -/// The copilot-api-token variant of . +/// One diagnostic emitted while ingesting managed settings. Returned rather than logged by the runtime so the host decides where ingestion diagnostics go, which matters because an ingestion runs before a session exists to carry a log sink. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthInfoCopilotApiToken : AuthInfo +internal sealed class ManagedSettingsLogMessage { - /// - [JsonIgnore] - public override string Type => "copilot-api-token"; - - /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("copilotUser")] - public CopilotUserResponse? CopilotUser { get; set; } + /// Severity of this diagnostic. + [JsonPropertyName("level")] + public ManagedSettingsLogLevel Level { get; set; } - /// Authentication host (always the public GitHub host). - [JsonPropertyName("host")] - public required string Host { get; set; } + /// Human-readable diagnostic text. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; } -/// Authentication-info variant for OAuth user auth, with host and login; the token remains in the runtime secret store. -/// The user variant of . +/// The managed policy layers an ingestion resolved, together with the per-layer outcome flags a host needs to decide how much to trust the result. The flags are reported rather than folded into the settings because a host must be able to distinguish an unmanaged device from a managed device whose policy could not be confirmed, and apply its fail-closed gates in the second case. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthInfoUser : AuthInfo +internal sealed class ManagedSettingsIngestResult { - /// - [JsonIgnore] - public override string Type => "user"; + /// The device layer could not be read, so it is unconfirmed rather than known-absent. + [JsonPropertyName("deviceLoadFailed")] + public bool DeviceLoadFailed { get; set; } - /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("copilotUser")] - public CopilotUserResponse? CopilotUser { get; set; } + /// The device layer discovered from the machine, when one exists. + [JsonPropertyName("deviceResponse")] + public JsonElement? DeviceResponse { get; set; } - /// Authentication host. - [JsonPropertyName("host")] - public required string Host { get; set; } + /// The sandbox floor contributed by the device layer. + [JsonPropertyName("deviceSandboxFloor")] + public JsonElement? DeviceSandboxFloor { get; set; } - /// OAuth user login. - [JsonPropertyName("login")] - public required string Login { get; set; } -} + /// Whether the device layer's sandbox intent could not be determined, so a consumer must not read its absence as permission to run unsandboxed. + [JsonPropertyName("deviceSandboxUndetermined")] + public bool DeviceSandboxUndetermined { get; set; } -/// An interactive account whose model provider owns its credentials. It carries no GitHub credential. -/// The account variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthInfoAccount : AuthInfo -{ - /// - [JsonIgnore] - public override string Type => "account"; + /// A device-managed policy is known to exist. + [JsonPropertyName("deviceSourceFound")] + public bool DeviceSourceFound { get; set; } - /// Host coordinate owned by the account's model provider. - [JsonPropertyName("host")] - public required string Host { get; set; } + /// Diagnostics emitted while resolving the layers, in the order they occurred. + [JsonPropertyName("logMessages")] + public IList LogMessages { get => field ??= []; set; } - /// Login identifying the provider-owned account. - [JsonPropertyName("login")] - public required string Login { get; set; } + /// The effective managed permissions layer, when the resolved policy carries one. + [JsonPropertyName("permissions")] + public JsonElement? Permissions { get; set; } + + /// Which channel registered the policy helper that produced the helper layer. + [JsonPropertyName("policyHelperAuthority")] + public ManagedSettingsPolicyHelperAuthority? PolicyHelperAuthority { get; set; } + + /// A policy helper is configured for this host. + [JsonPropertyName("policyHelperConfigured")] + public bool PolicyHelperConfigured { get; set; } + + /// The policy helper's failure must be treated as fail-closed rather than ignored. + [JsonPropertyName("policyHelperFailClosed")] + public bool PolicyHelperFailClosed { get; set; } + + /// The policy helper was configured but could not be run or its output could not be read. + [JsonPropertyName("policyHelperLoadFailed")] + public bool PolicyHelperLoadFailed { get; set; } + + /// The layer produced by the platform policy helper, when one ran. + [JsonPropertyName("policyHelperResponse")] + public JsonElement? PolicyHelperResponse { get; set; } + + /// A live fetch failed but a recent cached entry was served in its place, so `serverFetchFailed` stays false. Lets a consumer distinguish a clean fetch from a cache-backed fallback. + [JsonPropertyName("serverCacheFallback")] + public bool ServerCacheFallback { get; set; } + + /// Minimum client version requested by a successful response. Advisory only; the delivered policy remains active. + [JsonPropertyName("serverClientUpdateRequested")] + public string? ServerClientUpdateRequested { get; set; } + + /// The server refused to deliver a policy because this client is too old to enforce it, carrying the user-facing sentence describing the refusal. No retry will help; the remedy is a client update. Gates that exist only because of managed policy should deny on this rather than falling back to their unmanaged default. + [JsonPropertyName("serverClientUpdateRequired")] + public string? ServerClientUpdateRequired { get; set; } + + /// The error that made the server fetch fail, when one did. + [JsonPropertyName("serverFetchError")] + public string? ServerFetchError { get; set; } + + /// The server fetch failed and no cached entry stood in for it, so the server layer is unconfirmed and fail-closed gates apply. + [JsonPropertyName("serverFetchFailed")] + public bool ServerFetchFailed { get; set; } + + /// The server fetch failed while a policy demanded a fresh confirmation, which is the strict posture an enterprise opted into rather than an ordinary transient failure. + [JsonPropertyName("serverForcedRefreshFailure")] + public bool ServerForcedRefreshFailure { get; set; } + + /// The synthesized fail-closed permissions layer applied in place of a policy a refused client may not enforce. + [JsonPropertyName("serverPermissionsFloor")] + public JsonElement? ServerPermissionsFloor { get; set; } + + /// How the server layer was resolved. + [JsonPropertyName("serverResolution")] + public ManagedSettingsServerPolicyResolution ServerResolution { get; set; } + + /// The account's server-delivered policy, when one was resolved. + [JsonPropertyName("serverResponse")] + public JsonElement? ServerResponse { get; set; } + + /// The server fetch failed in a way a later retry could succeed at, as opposed to a refusal that no retry will change. + [JsonPropertyName("serverRetryableFailure")] + public bool ServerRetryableFailure { get; set; } + + /// The sandbox object from a cached server policy that a forced refresh declined to serve in full. Folded back into the effective sandbox layer so an outage during a forced refresh still synthesizes the fail-closed sandbox floor from the administrator's known intent. + [JsonPropertyName("serverSandboxFloor")] + public JsonElement? ServerSandboxFloor { get; set; } + + /// A server-managed policy is known to exist even though `serverResponse` is intentionally unset. Consumers should OR this with the presence of `serverResponse` when deciding whether the device is managed, so a fresh start during an outage still applies the fail-closed gates instead of treating the device as unmanaged. + [JsonPropertyName("serverSourceFound")] + public bool ServerSourceFound { get; set; } + + /// No usable token was available, so no server fetch was attempted. + [JsonPropertyName("serverTokenUnavailable")] + public bool ServerTokenUnavailable { get; set; } + + /// The effective managed settings composed from every resolved layer. Omitted when no layer set anything, so an unmanaged host locks nothing. + [JsonPropertyName("settings")] + public JsonElement? Settings { get; set; } } -/// Authentication-info input variant for GitHub CLI credentials, carrying host, login, and the `gh auth token` value. -/// The gh-cli variant of . +/// Inputs describing the host and account an ingestion should resolve managed policy for. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthInfoGhCli : AuthInfo +internal sealed class ManagedSettingsIngestParams { - /// - [JsonIgnore] - public override string Type => "gh-cli"; + /// The authenticated identity to resolve policy for. Omitted before sign-in, in which case no server layer is fetched. + [JsonPropertyName("authInfo")] + public JsonElement? AuthInfo { get; set; } - /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("copilotUser")] - public CopilotUserResponse? CopilotUser { get; set; } + /// Whether to ignore a cached server policy when reading, forcing the resolution to come from a live fetch. + [JsonPropertyName("bypassCacheRead")] + public bool? BypassCacheRead { get; set; } - /// Authentication host. - [JsonPropertyName("host")] - public required string Host { get; set; } + /// Name of the embedding client, reported to the managed-settings service as `client_id` so it can refuse to deliver a policy this client is too old to enforce. Omit it and the runtime reports a generic identity; the runtime version sent alongside is the gate that actually matters. + [JsonPropertyName("clientName")] + public string? ClientName { get; set; } - /// User login as reported by `gh auth status`. - [JsonPropertyName("login")] - public required string Login { get; set; } + /// A device layer supplied by the host instead of discovered from the machine. Pair it with `hasDeviceResponse` so an explicit absence is distinguishable from an omitted override. + [JsonPropertyName("deviceResponse")] + public JsonElement? DeviceResponse { get; set; } - /// The token returned by `gh auth token`. Treat as a secret. - [JsonPropertyName("token")] - public required string Token { get; set; } + /// The host's environment variables, read for cache location and policy overrides. Supplied by the host rather than read from the runtime process so a command that selected its own configuration home is honored. + [JsonPropertyName("environment")] + public JsonElement Environment { get; set; } + + /// Whether `deviceResponse` is an override at all. True with a null `deviceResponse` asserts the device layer is empty; false discovers it from the machine. + [JsonPropertyName("hasDeviceResponse")] + public bool? HasDeviceResponse { get; set; } + + /// The host's home directory, used to resolve cache and managed-file locations. + [JsonPropertyName("homeDirectory")] + public string HomeDirectory { get; set; } = string.Empty; + + /// The host's platform, in Node's `process.platform` spelling, used to locate platform-specific cache and policy paths. + [JsonPropertyName("platform")] + public string Platform { get; set; } = string.Empty; + + /// Whether a configured platform policy helper may be run to produce an enforced helper layer. + [JsonPropertyName("policyHelperEnabled")] + public bool? PolicyHelperEnabled { get; set; } + + /// The policy-helper invocation reason. Defaults to sessionStart. + [JsonPropertyName("policyHelperReason")] + public ManagedSettingsPolicyHelperReason? PolicyHelperReason { get; set; } + + /// Directory the policy helper runs in when this ingestion is not owned by a registered session. A host resolving policy before its session exists must pass this, or a directory-sensitive helper judges the wrong repository. + [JsonPropertyName("policyHelperWorkingDirectory")] + public string? PolicyHelperWorkingDirectory { get; set; } + + /// Whether to run ingestion. Omit or set false to return an empty result without discovering or fetching policy. + [JsonPropertyName("selfFetch")] + public bool? SelfFetch { get; set; } + + /// How this ingestion may use the on-disk server-policy cache. Defaults to the runtime's own policy when omitted. + [JsonPropertyName("serverPolicyCacheMode")] + public ManagedSettingsServerPolicyCacheMode? ServerPolicyCacheMode { get; set; } + + /// The session whose credential store and working directory the policy helper should use. Omit it when no session exists yet and the process-wide store applies. + [JsonPropertyName("sessionId")] + public string? SessionId { get; set; } + + /// User agent to send with the policy fetch. + [JsonPropertyName("userAgent")] + public string UserAgent { get; set; } = string.Empty; } -/// Authentication-info input variant for API-key authentication to a non-GitHub LLM provider, carrying the secret `apiKey` and host. -/// The api-key variant of . +/// The verdict of the `forceLoginOrgs` enterprise sign-in pin for one credential. A managed device may pin Copilot to an approved set of GitHub organizations so a developer cannot sign in with a personal — or another enterprise's — account and escape every control that keys off the signed-in identity. A host must refuse to proceed when `allowed` is false; the remaining fields explain the refusal rather than qualifying it. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthInfoApiKey : AuthInfo +internal sealed class ForceLoginOrgsDecision { - /// - [JsonIgnore] - public override string Type => "api-key"; + /// Whether the client may proceed. True whenever no pin is in force, so an unmanaged host is never blocked. + [JsonPropertyName("allowed")] + public bool Allowed { get; set; } - /// The API key. Treat as a secret. - [JsonPropertyName("apiKey")] - public required string ApiKey { get; set; } + /// A stable kebab-case reason code when refused: `org-not-approved`, `unverifiable-credential`, `policy-undetermined`, `policy-invalid`, `not-signed-in`, or `membership-unknown`. Omitted when allowed. Treat an unrecognized code as a refusal rather than mapping it to an allow: a code this host has not been taught about still denied the credential. + [JsonPropertyName("denial")] + public string? Denial { get; set; } - /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("copilotUser")] - public CopilotUserResponse? CopilotUser { get; set; } + /// Whether a pin applies at all. Distinguishes "allowed because unmanaged" from "allowed because the account is approved", which a diagnostics surface needs and which also decides whether a bring-your-own-key provider may serve inference. + [JsonPropertyName("enforced")] + public bool Enforced { get; set; } - /// Authentication host. - [JsonPropertyName("host")] - public required string Host { get; set; } + /// The approved organization the account matched, when allowed under a pin. + [JsonPropertyName("matchedOrg")] + public string? MatchedOrg { get; set; } + + /// A developer-facing explanation of the refusal. Omitted when allowed. + [JsonPropertyName("message")] + public string? Message { get; set; } + + /// Error-level detail from managed-policy discovery — which file failed to load, which field failed validation. Carried on the decision rather than left to the log because a refusal can exit the process before a log sink exists, which would discard the only actionable diagnostic a misconfigured managed device produces. + [JsonPropertyName("policyDiagnostics")] + public IList PolicyDiagnostics { get => field ??= []; set; } + + /// The approved organization logins, as configured. Enterprise configuration rather than user data, so it is safe to name in a message or a log line. + [JsonPropertyName("requiredOrgs")] + public IList RequiredOrgs { get => field ??= []; set; } } -/// Current authentication state. +/// The startup policy a resolution composed, the per-layer outcome flags a host needs to decide how much to trust it, and the enterprise sign-in verdict for the credential it ran against. The flags are reported rather than folded into the settings because a host must distinguish an unmanaged device from a managed device whose policy could not be confirmed, and apply its fail-closed gates in the second case. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AccountGetCurrentAuthResult +internal sealed class ManagedSettingsResolveStartupPolicyResult { - /// Authentication errors from the last auth attempt, if any. - [JsonPropertyName("authErrors")] - public IList? AuthErrors { get; set; } + /// Advisory minimum client version from a successful server response. + [JsonPropertyName("clientUpdateRequested")] + public string? ClientUpdateRequested { get; set; } - /// Current authentication information, if authenticated. - [JsonPropertyName("authInfo")] - public AuthInfo? AuthInfo { get; set; } + /// The server refused to deliver a policy because this client is too old to enforce it, carrying the user-facing sentence describing the refusal. No retry will help; the remedy is a client update. + [JsonPropertyName("clientUpdateRequired")] + public string? ClientUpdateRequired { get; set; } + + /// The device layer could not be read, so it is unconfirmed rather than known-absent. + [JsonPropertyName("deviceLoadFailed")] + public bool DeviceLoadFailed { get; set; } + + /// The device layer discovered from the machine, when one exists. + [JsonPropertyName("deviceResponse")] + public JsonElement? DeviceResponse { get; set; } + + /// The sandbox floor contributed by the device layer. + [JsonPropertyName("deviceSandboxFloor")] + public JsonElement? DeviceSandboxFloor { get; set; } + + /// The enterprise sign-in verdict for the credential this resolution ran against, evaluated over the effective policy so a server-delivered pin is honored alongside a device one. Computed on every resolution, including one that was not retained, so a host that declines to apply policy still cannot proceed past a pin. + [JsonPropertyName("forceLoginOrgs")] + public ForceLoginOrgsDecision ForceLoginOrgs { get => field ??= new(); set; } + + /// A managed policy is known to apply to this host, from a discovered source or a resolved document. + [JsonPropertyName("managedPolicyPresent")] + public bool ManagedPolicyPresent { get; set; } + + /// The permissions layer specifically could not be confirmed, so permission gates apply their fail-closed posture. + [JsonPropertyName("permissionPolicyUndetermined")] + public bool PermissionPolicyUndetermined { get; set; } + + /// The policy helper was configured but could not be run, or its output could not be read. + [JsonPropertyName("policyHelperLoadFailed")] + public bool PolicyHelperLoadFailed { get; set; } + + /// The layer produced by the platform policy helper, when one ran. + [JsonPropertyName("policyHelperResponse")] + public JsonElement? PolicyHelperResponse { get; set; } + + /// Some layer of the policy could not be confirmed, so its absence must not be read as permission. Folds in a failed server fetch as well as an unreadable device layer. + [JsonPropertyName("policyUndetermined")] + public bool PolicyUndetermined { get; set; } + + /// The sandbox was enabled because policy could not be confirmed rather than because an administrator asked for it. Lets a host explain a sandbox the user did not configure. + [JsonPropertyName("sandboxEnabledByUndeterminedPolicy")] + public bool SandboxEnabledByUndeterminedPolicy { get; set; } + + /// The effective sandbox floor composed across every layer, which a host must apply rather than its own default. + [JsonPropertyName("sandboxFloor")] + public JsonElement? SandboxFloor { get; set; } + + /// The server fetch failed and no cached entry stood in for it, so the server layer is unconfirmed and fail-closed gates apply. + [JsonPropertyName("serverFetchFailed")] + public bool ServerFetchFailed { get; set; } + + /// How the current runtime resolved the server layer. + [JsonPropertyName("serverResolution")] + public ManagedSettingsServerPolicyResolution ServerResolution { get; set; } + + /// The account's server-delivered policy, when one was resolved. + [JsonPropertyName("serverResponse")] + public JsonElement? ServerResponse { get; set; } + + /// Telemetry describing which layers answered and how, for the host to report alongside its own startup events. + [JsonPropertyName("telemetry")] + public JsonElement Telemetry { get; set; } + + /// Warnings raised while resolving, in the order they occurred. + [JsonPropertyName("warnings")] + public IList Warnings { get => field ??= []; set; } } -/// Authenticated account entry returned by `account.getAllUsers`. +/// Inputs describing the host and account a startup policy resolution should run for. Mirrors `managedSettings.ingest`, and adds the decision of whether the resolved policy is retained for this runtime process. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AccountAllUsers +internal sealed class ManagedSettingsResolveStartupPolicyParams { - /// Authentication information for this user. + /// The authenticated identity to resolve policy for. Omitted before sign-in, in which case no server layer is fetched and the sign-in pin is evaluated against no credential. [JsonPropertyName("authInfo")] - public AuthInfo AuthInfo { get => field ??= new(); set; } + public JsonElement? AuthInfo { get; set; } - /// Opaque identifier accepted by account and model selection APIs. - [JsonPropertyName("selectionId")] - public string? SelectionId { get; set; } + /// Whether to ignore a cached server policy when reading, forcing the resolution to come from a live fetch. + [JsonPropertyName("bypassCacheRead")] + public bool? BypassCacheRead { get; set; } - /// Associated token, if available. - [JsonPropertyName("token")] - public string? Token { get; set; } + /// Name of the embedding client, reported to the managed-settings service as `client_id` so it can refuse to deliver a policy this client is too old to enforce. + [JsonPropertyName("clientName")] + public string? ClientName { get; set; } + + /// A device layer supplied by the host instead of discovered from the machine. Pair it with `hasDeviceResponse` so an explicit absence is distinguishable from an omitted override. + [JsonPropertyName("deviceResponse")] + public JsonElement? DeviceResponse { get; set; } + + /// The host's environment variables, read for cache location, policy overrides, and the variables that would make a membership snapshot forgeable. + [JsonPropertyName("environment")] + public JsonElement Environment { get; set; } + + /// Whether `deviceResponse` is an override at all. True with a null `deviceResponse` asserts the device layer is empty; false discovers it from the machine. + [JsonPropertyName("hasDeviceResponse")] + public bool? HasDeviceResponse { get; set; } + + /// The host's home directory, used to resolve cache and managed-file locations. + [JsonPropertyName("homeDirectory")] + public string HomeDirectory { get; set; } = string.Empty; + + /// The host's platform, in Node's `process.platform` spelling, used to locate platform-specific cache and policy paths. + [JsonPropertyName("platform")] + public string Platform { get; set; } = string.Empty; + + /// Whether a configured platform policy helper may be run to produce an enforced helper layer. + [JsonPropertyName("policyHelperEnabled")] + public bool? PolicyHelperEnabled { get; set; } + + /// The policy-helper invocation reason. Defaults to sessionStart. + [JsonPropertyName("policyHelperReason")] + public ManagedSettingsPolicyHelperReason? PolicyHelperReason { get; set; } + + /// Directory the policy helper runs in when this resolution is not owned by a registered session. A host resolving policy before its session exists must pass this, or a directory-sensitive helper judges the wrong repository. + [JsonPropertyName("policyHelperWorkingDirectory")] + public string? PolicyHelperWorkingDirectory { get; set; } + + /// Whether to retain the resolved policy as this runtime process's startup policy, so later work in the process enforces it. Set false to resolve without side effects, which is what a protocol host wants before session creation: its sessions apply the complete policy through the normal lifecycle, and retaining here would let a readiness credential affect unrelated sessions. The returned verdict is identical either way — this decides only whether the process keeps it. + [JsonPropertyName("retainAndApply")] + public bool RetainAndApply { get; set; } + + /// Whether to run ingestion. Omit or set false to return an empty result without discovering or fetching policy. + [JsonPropertyName("selfFetch")] + public bool? SelfFetch { get; set; } + + /// How this resolution may use the on-disk server-policy cache. Defaults to the runtime's own policy when omitted. Rejected when combined with `bypassCacheRead`, because the two express the same decision and accepting both would leave the caller unsure which one the runtime honored. + [JsonPropertyName("serverPolicyCacheMode")] + public ManagedSettingsServerPolicyCacheMode? ServerPolicyCacheMode { get; set; } + + /// The session whose credential store, working directory and client kind the resolution should use. Omit it when no session exists yet and the process-wide store applies. A resolution whose session names a credential other than `authInfo` is returned but not retained, so a stale sign-in cannot apply policy for an account that has already been switched away from. + [JsonPropertyName("sessionId")] + public string? SessionId { get; set; } + + /// User agent to send with the policy fetch. + [JsonPropertyName("userAgent")] + public string UserAgent { get; set; } = string.Empty; } -/// Result of a successful login; throws on failure. +/// The managed remote-control policy in force for an account, if any. An absent policy means the account is unmanaged for this capability, which a host reads as allowed — so absence here is a positive statement that no policy applies, never a stand-in for a resolution that failed. A resolution that cannot be completed is rejected instead. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AccountLoginResult +internal sealed class ManagedSettingsLoadRemoteControlResult { - /// Whether the credential was persisted to a secure store (system keychain, or the config file when plaintext storage is enabled). False when no secure store was available and the token was not saved, so the consumer can decide how to proceed. - [JsonPropertyName("storedInVault")] - public bool StoredInVault { get; set; } + /// The account's `remoteControl` policy document. Absent when no managed policy governs remote control for this account. Reported as an explicit disabled policy, rather than as absence, when the service refused to deliver a policy this client is too old to enforce. + [JsonPropertyName("remoteControl")] + public JsonElement? RemoteControl { get; set; } } -/// Credentials to validate and store. Omit login to resolve the authenticated user from the token. +/// Inputs describing the host and account whose managed remote-control policy should be resolved. Mirrors `managedSettings.ingest`, and deliberately carries no retention decision: reading this gate never becomes this runtime process's startup policy. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AccountLoginRequest +internal sealed class ManagedSettingsLoadRemoteControlParams { - /// GitHub host URL. - [JsonPropertyName("host")] - public string Host { get; set; } = string.Empty; + /// The authenticated identity to resolve policy for. Omitted before sign-in, in which case no server layer is fetched. + [JsonPropertyName("authInfo")] + public JsonElement? AuthInfo { get; set; } - /// User login/username. When omitted, the runtime validates the token and resolves the login from GitHub. - [JsonPropertyName("login")] - public string? Login { get; set; } + /// Name of the embedding client, reported to the managed-settings service as `client_id` so it can refuse to deliver a policy this client is too old to enforce. That refusal is exactly what turns into an explicit `disabled` below, so a host that omits this field forfeits the protection. + [JsonPropertyName("clientName")] + public string? ClientName { get; set; } - /// GitHub authentication token. - [JsonPropertyName("token")] - public string Token { get; set; } = string.Empty; -} + /// A device layer supplied by the host instead of discovered from the machine. Pair it with `hasDeviceResponse` so an explicit absence is distinguishable from an omitted override. + [JsonPropertyName("deviceResponse")] + public JsonElement? DeviceResponse { get; set; } -/// Logout result indicating if more users remain. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AccountLogoutResult -{ - /// Whether other authenticated users remain after logout. - [JsonPropertyName("hasMoreUsers")] - public bool HasMoreUsers { get; set; } + /// The host's environment variables, read for cache location and policy overrides. + [JsonPropertyName("environment")] + public JsonElement Environment { get; set; } + + /// Whether `deviceResponse` is an override at all. True with a null `deviceResponse` asserts the device layer is empty; false discovers it from the machine. + [JsonPropertyName("hasDeviceResponse")] + public bool? HasDeviceResponse { get; set; } + + /// The host's home directory, used to resolve cache and managed-file locations. + [JsonPropertyName("homeDirectory")] + public string HomeDirectory { get; set; } = string.Empty; + + /// The host's platform, in Node's `process.platform` spelling, used to locate platform-specific cache and policy paths. + [JsonPropertyName("platform")] + public string Platform { get; set; } = string.Empty; + + /// Whether a configured platform policy helper may be run to produce an enforced helper layer. + [JsonPropertyName("policyHelperEnabled")] + public bool? PolicyHelperEnabled { get; set; } + + /// The policy-helper invocation reason. Defaults to sessionStart. + [JsonPropertyName("policyHelperReason")] + public ManagedSettingsPolicyHelperReason? PolicyHelperReason { get; set; } + + /// Whether to run ingestion. Omit or set false to return an empty result without discovering or fetching policy. + [JsonPropertyName("selfFetch")] + public bool? SelfFetch { get; set; } + + /// User agent to send with the policy fetch. + [JsonPropertyName("userAgent")] + public string UserAgent { get; set; } = string.Empty; } -/// User to log out. +/// The credential and environment to evaluate the device-channel sign-in pin against. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AccountLogoutRequest +internal sealed class ManagedSettingsResolveDeviceForceLoginOrgsGateParams { - /// Authentication information for the user to log out. + /// The authenticated identity to judge. Omitted when signed out, which is the case the device channel exists to serve: it can refuse a first sign-in before any credential exists. [JsonPropertyName("authInfo")] - public AuthInfo? AuthInfo { get; set; } + public JsonElement? AuthInfo { get; set; } - /// Opaque account identifier returned by `account.getAllUsers`. - [JsonPropertyName("selectionId")] - public string? SelectionId { get; set; } + /// The host's environment variables. Read for the variables that would make an identity's membership snapshot forgeable, so a credential whose provenance cannot be trusted is refused rather than accepted. + [JsonPropertyName("environment")] + public JsonElement Environment { get; set; } } -/// Confirmation that the secret values were registered. +/// Validated device-managed settings discovered before a session exists. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SecretsAddFilterValuesResult +public sealed class ManagedSettingsReadResult { - /// Whether the values were successfully registered. - [JsonPropertyName("ok")] - public bool Ok { get; set; } + /// Discovery or validation error text when managed settings could not be read safely. + [JsonPropertyName("errorMessage")] + public string? ErrorMessage { get; set; } + + /// Validated, canonical managed-settings JSON. Omitted when no managed settings were discovered or when discovered settings failed validation. + [JsonPropertyName("settingsJson")] + public JsonElement? SettingsJson { get; set; } } -/// Secret values to add to the redaction filter. +/// One validation finding for a managed-settings document. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SecretsAddFilterValuesRequest +public sealed class ManagedSettingsDiagnostic { - /// Raw secret values to register for redaction. - [JsonPropertyName("values")] - public IList Values { get => field ??= []; set; } + /// Human-readable description of the finding. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; + + /// Dot-separated path of the offending setting, such as `autoTier.overridable`. Empty for the document as a whole. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + + /// Whether the finding rejects the document. + [JsonPropertyName("severity")] + public ManagedSettingsDiagnosticSeverity Severity { get; set; } } -/// Concrete configuration file containing an MCP server declaration. +/// One managed-settings channel and the document it delivered. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpSourceFile +public sealed class ManagedSettingsLayer { - /// RFC 6901 JSON Pointer to the server declaration, when known. - [JsonPropertyName("jsonPointer")] - public string? JsonPointer { get; set; } + /// Validated managed-settings document this channel delivered. Absent when the channel delivered none. + [JsonPropertyName("settings")] + public JsonElement? Settings { get; set; } - /// Canonical file URI for the configuration document. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("uri")] - public string Uri { get; set; } = string.Empty; + /// Channel identifier: `device` (MDM, plist, registry, or managed file), `server` (account or organization policy), or `policyHelper` (session-local helper output, supported by compose). Treat unknown output values as additional channels; more may be added. + [JsonPropertyName("source")] + public string Source { get; set; } = string.Empty; } -/// Plugin identity associated with an MCP server declaration. +/// Lock state and provenance of one managed setting. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpSourcePlugin +public sealed class ManagedSettingMeta { - /// Canonical plugin identity. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Whether users and repositories may choose a different value. `false` means policy locks the value. + [JsonPropertyName("overridable")] + public bool Overridable { get; set; } - /// Human-readable plugin name, when available. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("name")] - public string? Name { get; set; } + /// Original managed value when the runtime adjusted it to a supported effective value. Omitted when no adjustment was needed. + [JsonPropertyName("requested")] + public string? Requested { get; set; } - /// Plugin version, when available. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("version")] - public string? Version { get; set; } + /// Channel that supplied this scalar value, matching a `layers[].source`: `device`, `server`, or `policyHelper`. These scalar defaults select one winning channel, not a mixed source. Treat unknown values as additional channels; more may be added. + [JsonPropertyName("source")] + public string Source { get; set; } = string.Empty; } -/// Canonical identity and location of the effective MCP server declaration. The declaration is uniquely addressed by this source id together with the discovered server name. +/// Per-key lock state and provenance for `ManagedSettingsValues`, with the same field names. Producers emit each typed key in values and meta together; both outer objects are omitted when no typed key is set. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpSourceRef +public sealed class ManagedSettingsMeta { - /// Open semantic editability identifier. Known values are editable and read-only. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("editability")] - public string Editability { get; set; } = string.Empty; - - /// Configuration file location, when the declaration is file-backed. - [JsonPropertyName("file")] - public McpSourceFile? File { get; set; } + /// Lock state and provenance of `values.autoTier`. + [JsonPropertyName("autoTier")] + public ManagedSettingMeta? AutoTier { get; set; } - /// Opaque stable identity for the configuration source. Clients must not parse this value. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Lock state and provenance of `values.contextTier`. + [JsonPropertyName("contextTier")] + public ManagedSettingMeta? ContextTier { get; set; } - /// Open source-kind identifier. Known values include user, workspace, invocation, plugin, builtin, and device-registry. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("kind")] - public string Kind { get; set; } = string.Empty; + /// Lock state and provenance of `values.effortLevel`. + [JsonPropertyName("effortLevel")] + public ManagedSettingMeta? EffortLevel { get; set; } - /// Plugin identity, when the declaration is plugin-provided. - [JsonPropertyName("plugin")] - public McpSourcePlugin? Plugin { get; set; } + /// Lock state and provenance of `values.model`. + [JsonPropertyName("model")] + public ManagedSettingMeta? Model { get; set; } } -/// MCP server discovered by `mcp.discover`, with config source, optional plugin source, transport type, and enabled state. +/// Effective enterprise managed settings and contributing channels. Session events report applied policy; sessionless resolve reports an account/device snapshot, and compose reports a non-applying preview of candidate documents. Device values take precedence over server values, then the policy helper, per ordinary key, while permissions compose restrictively. Session-local SDK-client policy is included only in session results. Marked experimental while the managed-settings surface stabilizes. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DiscoveredMcpServer +public sealed class ManagedSettingsResolvedData { - /// Canonical identity and location of the effective server declaration. - [JsonPropertyName("effectiveSource")] - public McpSourceRef? EffectiveSource { get; set; } + /// Whether an explicit enterprise policy restriction disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers; an unresolved policy does not force this on by itself. + [JsonPropertyName("bypassPermissionsDisabled")] + public bool BypassPermissionsDisabled { get; set; } - /// Whether the server is enabled (not in the disabled list). - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } + /// Whether a session-local permissions layer injected by the SDK host was present. + [JsonPropertyName("clientManaged")] + public bool? ClientManaged { get; set; } - /// Server name (config key). - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Whether an actual device MDM/plist/registry/file managed-settings layer was present. + [JsonPropertyName("deviceManaged")] + public bool DeviceManaged { get; set; } - /// Configuration source: user, workspace, plugin, or builtin. - [JsonPropertyName("source")] - public McpServerSource Source { get; set; } + /// Whether managed policy could not be determined (e.g. a failed server fetch) and unresolved-policy safeguards remain active. This does not by itself disable bypass-permissions; `bypassPermissionsDisabled` reports only an explicit policy restriction. + [JsonPropertyName("failClosed")] + public bool FailClosed { get; set; } - /// Plugin name that provided this server, when source is plugin. - [JsonPropertyName("sourcePlugin")] - public string? SourcePlugin { get; set; } + /// The setting keys under enterprise management in the effective managed settings (e.g. `model`, `enabledPlugins`, `permissions`). Empty when no managed settings are in force. + [JsonPropertyName("managedKeys")] + public IList ManagedKeys { get => field ??= []; set; } - /// Plugin version that provided this server, when source is plugin. - [JsonPropertyName("sourcePluginVersion")] - public string? SourcePluginVersion { get; set; } + /// Whether at least two managed sources supplied permission allowlists, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. + [JsonPropertyName("permissionsAllowIntersected")] + public bool? PermissionsAllowIntersected { get; set; } - /// Server transport type: stdio, http, sse (deprecated), or memory. - [JsonPropertyName("type")] - public DiscoveredMcpServerType? Type { get; set; } -} + /// Reusable retained managed permission policy for managedSettings.permissions.evaluate, including source composition and session-local injection. Provided by live session snapshots; absent in older events. Refresh the context when managed policy changes. + [JsonPropertyName("permissionsContext")] + public ManagedPermissionsContext? PermissionsContext { get; set; } -/// MCP servers discovered from user, workspace, plugin, and built-in sources. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpDiscoverResult -{ - /// MCP servers discovered from all sources. - [JsonPropertyName("servers")] - public IList Servers { get => field ??= []; set; } -} + /// Whether the policy-helper managed-settings layer was present. The policy helper is the weakest channel: it fills keys no enterprise source set and can never replace one. + [JsonPropertyName("policyHelperManaged")] + public bool? PolicyHelperManaged { get; set; } -/// Optional working directory used as context for MCP server discovery. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpDiscoverRequest -{ - /// Whether to include canonical effectiveSource metadata for each discovered server. Callers must opt in so protocol-3 clients retain the legacy closed response shape. - [JsonPropertyName("includeEffectiveSource")] - public bool? IncludeEffectiveSource { get; set; } + /// Whether the effective sandbox policy forces the sandbox on *only* because managed policy could not be determined, rather than because the policy requires it. Lets clients tell a user whose `--no-sandbox` was overridden that the sandbox stayed on as a fail-closed fallback, instead of attributing it to an administrator who set no such policy. + [JsonPropertyName("sandboxEnabledByUndeterminedPolicy")] + public bool? SandboxEnabledByUndeterminedPolicy { get; set; } - /// Working directory used as context for discovery (e.g., plugin resolution). - [JsonPropertyName("workingDirectory")] - public string? WorkingDirectory { get; set; } + /// Whether the server (account/org) managed-settings layer was present. + [JsonPropertyName("serverManaged")] + public bool ServerManaged { get; set; } + + /// The effective (resolved) managed settings values, so clients can render exactly what is enforced. Absent when no managed policy is in force. + [JsonPropertyName("settings")] + public JsonElement? Settings { get; set; } + + /// Channel summary: `server`, `device`, `client`, or `policyHelper` when exactly one channel contributed; `mixed` when multiple channels contributed; otherwise `none`. Consult the per-channel booleans for exact provenance. + [JsonPropertyName("source")] + public ManagedSettingsResolvedSource Source { get; set; } } -/// Outcome of an mcp.planInstall call: either a normalised plan, or one typed refusal. Nothing is written in either case. -/// Polymorphic base type discriminated by kind. +/// Typed effective values of managed settings. Each field mirrors the managed-settings schema key of the same name; more keys are added as they are typed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(McpPlanInstallResultPlanned), "planned")] -[JsonDerivedType(typeof(McpPlanInstallResultNegotiationRefused), "negotiation-refused")] -[JsonDerivedType(typeof(McpPlanInstallResultHandleRejected), "handle-rejected")] -[JsonDerivedType(typeof(McpPlanInstallResultInvalidRequest), "invalid-request")] -[JsonDerivedType(typeof(McpPlanInstallResultAuthenticationRequired), "authentication-required")] -[JsonDerivedType(typeof(McpPlanInstallResultPolicyRejected), "policy-rejected")] -[JsonDerivedType(typeof(McpPlanInstallResultNetworkFailure), "network-failure")] -[JsonDerivedType(typeof(McpPlanInstallResultUnsafeRetrieval), "unsafe-retrieval")] -[JsonDerivedType(typeof(McpPlanInstallResultMalformedCard), "malformed-card")] -[JsonDerivedType(typeof(McpPlanInstallResultContractViolation), "contract-violation")] -[JsonDerivedType(typeof(McpPlanInstallResultUnavailableTransport), "unavailable-transport")] -[JsonDerivedType(typeof(McpPlanInstallResultNotInstallable), "not-installable")] -[JsonDerivedType(typeof(McpPlanInstallResultUnavailable), "unavailable")] -public partial class McpPlanInstallResult +public sealed class ManagedSettingsValues { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// Managed Auto routing preference, used when the selected model is `auto`. + [JsonPropertyName("autoTier")] + public AutoTier? AutoTier { get; set; } + /// Managed context-tier default for the managed concrete model. + [JsonPropertyName("contextTier")] + public ContextTier? ContextTier { get; set; } -/// The protocol version and capability set the runtime actually honoured for a successful catalog operation. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CatalogNegotiatedContract -{ - /// Wire features the runtime understood for this operation. Includes the five original catalog capabilities and only explicitly requested supported additions, in supported order without duplicates. Capabilities that introduce new success-union variants or operations are therefore included only when explicitly required, preserving older protocol-v3 clients. Always a superset of the caller's required features, because any shortfall is a refusal instead. Operation availability remains a separate typed result. - [JsonPropertyName("grantedCapabilities")] - public IList GrantedCapabilities { get => field ??= []; set; } + /// Managed reasoning-effort default for the managed concrete model. The runtime clamps it to an entitled effort when model availability is known. + [JsonPropertyName("effortLevel")] + public string? EffortLevel { get; set; } - /// Protocol version of the runtime that served the request. - [JsonPropertyName("runtimeProtocolVersion")] - public long RuntimeProtocolVersion { get; set; } + /// Managed default model identifier. When model availability was resolved, aliases and family names are projected to a concrete available model ID; otherwise the configured value is returned. + [JsonPropertyName("model")] + public string? Model { get; set; } } -/// The configuration-change alternative for the transportChoices entry at the same index. Only the selected alternative is applied; entries are not cumulative. The payload stays behind the runtime boundary. +/// Effective enterprise managed settings for an account, resolved without a session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpPlanConfigurationChange +public sealed class ManagedSettingsResolveResult { - /// Names of the configuration fields the change would set, without their values. - [JsonPropertyName("changedFields")] - public IList ChangedFields { get => field ??= []; set; } + /// Printable opaque identity of the account the settings were resolved for, suitable for comparison and storage, not an account selectionId. Absent when no account was available, in which case only device policy is reported. + [JsonPropertyName("account")] + public string? Account { get; set; } - /// Configuration key the change applies to. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("configKey")] - public string ConfigKey { get; set; } = string.Empty; + /// Warnings about unavailable policy sources or a failed refresh served from cache. A cached response is not proof of a successful live fetch; `resolved.failClosed` separately describes enforcement. + [JsonPropertyName("diagnostics")] + public IList Diagnostics { get => field ??= []; set; } - /// Whether the change would create a new entry or modify an existing one. - [JsonPropertyName("operation")] - public McpPlanConfigurationOperation Operation { get; set; } + /// Each managed-settings channel consulted, strongest first, with the validated document it delivered before merging. `resolved.settings` is the merged result. More channels may be added over time. + [JsonPropertyName("layers")] + public IList Layers { get => field ??= []; set; } - /// Scope the change would be written to. - [JsonPropertyName("scope")] - public McpPlanScope Scope { get; set; } + /// Per-key lock state and provenance for the entries in `values`, using the same key names. + [JsonPropertyName("meta")] + public ManagedSettingsMeta? Meta { get; set; } - /// Secret placeholders the written configuration would reference. The constrained placeholder type cannot carry a literal secret value. - [JsonPropertyName("secretReferences")] - public IList SecretReferences { get => field ??= []; set; } + /// Reusable source-composed permission policy for managedSettings.permissions.evaluate. Unlike resolved.settings.permissions, this retains every source's allowlist and default-prompt semantics. Refresh through resolve when account/device policy changes. Absent on runtimes that do not support permission evaluation. + [JsonPropertyName("permissionsContext")] + public ManagedPermissionsContext? PermissionsContext { get; set; } + + /// Effective managed settings from the device and account (server) channels, in the same shape as `session.managedSettings.get`, excluding session-local injection. + [JsonPropertyName("resolved")] + public ManagedSettingsResolvedData Resolved { get => field ??= new(); set; } + + /// Typed effective values of managed settings, keyed like the managed-settings schema and already resolved across channels, with the `{ "overridable": ... }` wrapper removed. Present when policy sets at least one typed key. Keys not typed here are available in `resolved.settings`. + [JsonPropertyName("values")] + public ManagedSettingsValues? Values { get; set; } } -/// Normalised identity of the MCP server a plan targets, independent of how the card spelled it. +/// RPC data type for ManagedSettingsResolve operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpPlanResourceIdentity +internal sealed class ManagedSettingsResolveRequest { - /// Canonical, normalised name of the server, for example `io.github.owner/server`. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("canonicalName")] - public string CanonicalName { get; set; } = string.Empty; + /// Embedding client identity for server policy requests, as in session creation. Omit for the CLI identity. + [JsonPropertyName("clientName")] + public string? ClientName { get; set; } - /// Registry identifier of the server, when it came from a registry. - [JsonPropertyName("registryId")] - public string? RegistryId { get; set; } + /// GitHub token to resolve instead of the current account. The call fails when the token cannot be resolved. + [JsonPropertyName("gitHubToken")] + public string? GitHubToken { get; set; } - /// Local configuration key the server would be recorded under. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// Opaque account identifier returned by `account.getAllUsers`. When omitted, the current account is used, or device policy only when no account is signed in. + [JsonPropertyName("selectionId")] + public string? SelectionId { get; set; } - /// Version advertised by the card, when it declares one. - [JsonPropertyName("version")] - public string? Version { get; set; } + /// Working directory used to run an organization policy helper. When omitted, sessionless resolution does not run the helper. + [JsonPropertyName("workingDirectory")] + public string? WorkingDirectory { get; set; } } -/// Outcome of evaluating the planned server against registry and enterprise policy. Evaluation is read-only. +/// The authoring JSON schema for managed settings recognized by this runtime. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpPlanPolicyResult +public sealed class ManagedSettingsSchemaResult { - /// What policy decided for this server. - [JsonPropertyName("decision")] - public McpPlanPolicyDecision Decision { get; set; } + /// Version of the runtime that owns this schema. + [JsonPropertyName("runtimeVersion")] + public string RuntimeVersion { get; set; } = string.Empty; - /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("reason")] - public string? Reason { get; set; } + /// JSON schema (draft 2020-12) with descriptive shared `x-composition` annotations, not a complete runtime composition contract. Model, effortLevel, and contextTier remain coupled; use `managedSettings.compose` for the runtime's effective result. + [JsonPropertyName("schema")] + public JsonElement Schema { get; set; } +} - /// Which authority produced the decision. - [JsonPropertyName("source")] - public McpPlanPolicySource Source { get; set; } +/// Result of validating a managed-settings document. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ManagedSettingsValidateResult +{ + /// Errors that reject the document and warnings about content the runtime ignores. + [JsonPropertyName("diagnostics")] + public IList Diagnostics { get => field ??= []; set; } + + /// Canonical form of the document the runtime would apply, with unrecognized keys removed. Absent when the document is invalid. + [JsonPropertyName("settings")] + public JsonElement? Settings { get; set; } + + /// Whether the runtime would accept the document within the preview resource limits. Always equals whether `settings` is present. An invalid document is rejected as a whole. + [JsonPropertyName("valid")] + public bool Valid { get; set; } } -/// Semantic digest of a strictly parsed and schema-validated JSON MCP card. Both URL-backed and embedded cards are canonicalised with RFC 8785 JSON Canonicalization Scheme, encoded as UTF-8, and hashed with SHA-256. +/// A candidate managed-settings document to validate without applying it. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CardDigest +internal sealed class ManagedSettingsValidateRequest { - /// Digest algorithm and canonical representation. - [JsonPropertyName("algorithm")] - public CardDigestAlgorithm Algorithm { get; set; } + /// The document to validate: a JSON object, or a string containing the document's JSON text. Preview documents are limited to 1 MiB and 64 levels of nesting, a stricter resource limit than delivered-policy parsing; violations are returned as diagnostics. + [JsonPropertyName("content")] + public JsonElement Content { get; set; } - /// SHA-256 digest of the RFC 8785 canonical UTF-8 bytes, encoded as exactly 64 lowercase hexadecimal characters. - [RegularExpression("^[0-9a-f]{64}$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(64)] - [MaxLength(64)] - [JsonPropertyName("value")] - public string Value { get; set; } = string.Empty; + /// Channel the document is meant for (`device`, `server`, or `policyHelper`). Some keys are only honored in some channels; for example, a `policyHelper` registration is ignored in policy-helper output. When omitted, no channel-specific checks run. + [JsonPropertyName("layer")] + public string? Layer { get; set; } } -/// Provenance of the exact validated JSON MCP card content bound privately to a completed plan and its opaque handle. +/// The effective managed settings the runtime would enforce for the given documents, in the same shape `managedSettings.resolve` returns. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpPlanProvenance +public sealed class ManagedSettingsComposeResult { - /// Authority associated with the validated card, without path, query, or credentials. Inert untrusted data. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("authority")] - public string Authority { get; set; } = string.Empty; + /// Warnings about ignored content, with paths prefixed by the channel name. + [JsonPropertyName("diagnostics")] + public IList Diagnostics { get => field ??= []; set; } - /// Semantic digest of the exact validated JSON content bound to the plan handle. - [JsonPropertyName("cardDigest")] - public CardDigest CardDigest { get => field ??= new(); set; } + /// Only the supplied channels, strongest first, with canonical documents. Empty canonical documents are represented as absent settings, as in live resolution. + [JsonPropertyName("layers")] + public IList Layers { get => field ??= []; set; } - /// JSON MCP media type the validated card was interpreted as. - [JsonPropertyName("mediaType")] - public McpServerCardMediaType MediaType { get; set; } + /// Per-key lock state and provenance for `values`. + [JsonPropertyName("meta")] + public ManagedSettingsMeta? Meta { get; set; } - /// ISO 8601 timestamp at which the runtime completed strict parsing and schema validation of the card content. - [JsonPropertyName("validatedAt")] - public string ValidatedAt { get; set; } = string.Empty; + /// Effective managed settings, in the same shape as `session.managedSettings.get`. + [JsonPropertyName("resolved")] + public ManagedSettingsResolvedData Resolved { get => field ??= new(); set; } + + /// Typed effective values, as in `managedSettings.resolve`. + [JsonPropertyName("values")] + public ManagedSettingsValues? Values { get; set; } } -/// Where a plan would be written. +/// One candidate channel; absent settings represents a channel that delivered no document. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpPlanTarget +public sealed class ManagedSettingsComposeLayer { - /// Configuration key the server would be recorded under within that scope. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("configKey")] - public string ConfigKey { get; set; } = string.Empty; + /// Candidate managed-settings document. Omit when the channel delivered none, as in resolve output. + [JsonPropertyName("settings")] + public JsonElement? Settings { get; set; } - /// Configuration scope the plan targets. - [JsonPropertyName("scope")] - public McpPlanScope Scope { get; set; } + /// The channel whose candidate document is being supplied. + [JsonPropertyName("source")] + public ManagedSettingsChannel Source { get; set; } } -/// One eligible way to run the server, represented as a tagged package or remote variant so package identity and endpoint states cannot contradict the install method. -/// Polymorphic base type discriminated by installMethod. +/// Candidate managed-settings documents to merge without applying them. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "installMethod", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(McpPlanTransportChoicePackage), "package")] -[JsonDerivedType(typeof(McpPlanTransportChoiceRemote), "remote")] -public partial class McpPlanTransportChoice +internal sealed class ManagedSettingsComposeRequest { - /// The type discriminator. - [JsonPropertyName("installMethod")] - public virtual string InstallMethod { get; set; } = string.Empty; + /// One entry per channel. `source` must be `device`, `server`, or `policyHelper`, each at most once (checked at runtime); order does not matter, because channel precedence is fixed. To preview documents from resolve output, map recognized source strings to ManagedSettingsChannel and copy their settings; generated resolve and compose layer types are distinct. Omitted settings means this channel delivered no document. Supplied documents must be valid within the preview limits; warnings are returned in diagnostics. Compose does not reproduce source-failure state or retained enforcement floors from resolve. + [JsonPropertyName("layers")] + public IList Layers { get => field ??= []; set; } } - -/// One non-secret value a transport choice needs, represented as a scalar or enumerated variant so enum values cannot be missing or attached to another type. -/// Polymorphic base type discriminated by kind. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonPolymorphic( TypeDiscriminatorPropertyName = "kind", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(McpPlanRequiredValueScalar), "scalar")] -[JsonDerivedType(typeof(McpPlanRequiredValueEnum), "enum")] -public partial class McpPlanRequiredValue +[JsonDerivedType(typeof(ManagedPermissionOperationUrl), "url")] +public partial class ManagedPermissionOperation { /// The type discriminator. [JsonPropertyName("kind")] @@ -2347,2680 +2491,2915 @@ public partial class McpPlanRequiredValue } -/// One non-secret scalar value a transport choice needs before it can be applied. -/// The scalar variant of . +/// The url variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanRequiredValueScalar : McpPlanRequiredValue +public partial class ManagedPermissionOperationUrl : ManagedPermissionOperation { /// [JsonIgnore] - public override string Kind => "scalar"; - - /// Where the value is applied when the server is launched. - [JsonPropertyName("category")] - public required McpPlanValueCategory Category { get; set; } + public override string Kind => "url"; - /// Default supplied by the card, when the value can be resolved without input. Presence is the authoritative indication that a default exists. Inert untrusted data. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("defaultValue")] - public string? DefaultValue { get; set; } + /// Absolute HTTP(S) URL with a host. Invalid URLs reject the entire evaluation batch. + [JsonPropertyName("url")] + public required string Url { get; set; } +} - /// Human-readable explanation from the card. Inert untrusted text. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("description")] - public string? Description { get; set; } +/// RPC data type for ManagedPermissionEvaluation operations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ManagedPermissionEvaluation +{ + /// Original input operation associated with this verdict. + [JsonPropertyName("operation")] + public ManagedPermissionOperation Operation { get => field ??= new(); set; } - /// Whether the value may be supplied more than once. - [JsonPropertyName("isRepeated")] - public required bool IsRepeated { get; set; } + /// Managed-policy decision only; never bypasses user permissions or other security controls. + [JsonPropertyName("verdict")] + public ManagedPermissionVerdict Verdict { get; set; } +} - /// Key the value is supplied under. Inert untrusted data. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("key")] - public required string Key { get; set; } +/// Ordered managed permission verdicts and the supplied policy's fail-closed posture. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ManagedSettingsPermissionsEvaluateResult +{ + /// Managed permission policy could not be determined; every operation is denied. + [JsonPropertyName("failClosed")] + public bool FailClosed { get; set; } - /// Whether the value must be present for the plan to be applicable. - [JsonPropertyName("required")] - public required bool Required { get; set; } + /// One verdict per input operation, in the same order, including duplicates. + [JsonPropertyName("results")] + public IList Results { get => field ??= []; set; } +} - /// Human-readable label from the card. Inert untrusted text. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(200)] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("title")] - public string? Title { get; set; } +/// Managed policy context and ordered operations for pure, sessionless evaluation. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ManagedSettingsPermissionsEvaluateRequest +{ + /// Trusted, reusable managed permission policy snapshot; no policy is discovered during evaluation. + [JsonPropertyName("context")] + public ManagedPermissionsContext Context { get; set; } = null!; - /// Scalar type the value must conform to. - [JsonPropertyName("valueType")] - public required McpPlanScalarValueType ValueType { get; set; } + /// Operations to evaluate, preserving input order and duplicates. An empty batch is valid. + [JsonPropertyName("operations")] + public IList Operations { get => field ??= []; set; } } -/// One enumerated non-secret value a transport choice needs before it can be applied. The permitted values are structurally required. -/// The enum variant of . +/// Hosting capabilities and session capacity advertised by an environment. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanRequiredValueEnum : McpPlanRequiredValue +public sealed class EnvironmentCapabilities { - /// - [JsonIgnore] - public override string Kind => "enum"; + /// Advertised Agent Host Protocol version. + [JsonPropertyName("ahpVersion")] + public string? AhpVersion { get; set; } - /// Where the value is applied when the server is launched. - [JsonPropertyName("category")] - public required McpPlanValueCategory Category { get; set; } + /// Current session count, when advertised. + [JsonPropertyName("currentSessions")] + public long? CurrentSessions { get; set; } - /// Default supplied by the card, when the value can be resolved without input. Presence is the authoritative indication that a default exists. Inert untrusted data. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("defaultValue")] - public string? DefaultValue { get; set; } + /// Feature identifiers advertised by the environment. + [JsonPropertyName("features")] + public IList Features { get => field ??= []; set; } - /// Human-readable explanation from the card. Inert untrusted text. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("description")] - public string? Description { get; set; } + /// Maximum session capacity, when advertised. + [JsonPropertyName("maxSessions")] + public long? MaxSessions { get; set; } +} - /// Non-empty permitted value set. Inert untrusted data. - [JsonPropertyName("enumValues")] - public required IList EnumValues { get; set; } +/// Safe discovery information. Host-side relay bootstrap credentials are never included. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class GitHubEnvironment +{ + /// Hosting capabilities advertised by the environment. + [JsonPropertyName("capabilities")] + public EnvironmentCapabilities? Capabilities { get; set; } - /// Whether the value may be supplied more than once. - [JsonPropertyName("isRepeated")] - public required bool IsRepeated { get; set; } + /// Identifier assigned by Mission Control. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// Key the value is supplied under. Inert untrusted data. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("key")] - public required string Key { get; set; } + /// Compute kind reported by Mission Control. + [JsonPropertyName("kind")] + public EnvironmentKind Kind { get; set; } - /// Whether the value must be present for the plan to be applicable. - [JsonPropertyName("required")] - public required bool Required { get; set; } + /// Discovery labels attached to the environment. + [JsonPropertyName("labels")] + public IDictionary? Labels { get; set; } - /// Human-readable label from the card. Inert untrusted text. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(200)] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("title")] - public string? Title { get; set; } + /// Timestamp of the last heartbeat received by Mission Control. + [JsonPropertyName("lastHeartbeatAt")] + public string? LastHeartbeatAt { get; set; } - /// Discriminator: the value must be one of `enumValues`. - [JsonPropertyName("valueType")] - public required McpPlanEnumValueType ValueType { get; set; } -} + /// Human-readable environment name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; -/// A secret a transport choice needs, referenced by placeholder. No secret value ever appears in a plan, and the placeholder resolves against the keychain only when a plan is applied. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpPlanSecretPlaceholder -{ - /// Key the secret is supplied under. Inert untrusted data. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("key")] - public string Key { get; set; } = string.Empty; + /// Organization identifier, when the environment belongs to an organization. + [JsonPropertyName("orgId")] + public string? OrgId { get; set; } - /// The runtime-assigned `${secret:<id>}` placeholder written into configuration in place of the value. - [JsonPropertyName("placeholder")] - public string Placeholder { get; set; } = string.Empty; + /// Identifier of the environment owner. + [JsonPropertyName("ownerId")] + public string? OwnerId { get; set; } - /// Human-readable label from the card. Inert untrusted text. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(200)] - [JsonPropertyName("title")] - public string? Title { get; set; } + /// Owner category reported by Mission Control. + [JsonPropertyName("ownerType")] + public string? OwnerType { get; set; } + + /// Open-ended operational status vocabulary. + [JsonPropertyName("status")] + public string Status { get; set; } = string.Empty; } -/// An eligible local-package transport choice. Package identity is required and a remote endpoint cannot be represented. -/// The package variant of . +/// Environments visible to the authenticated caller and matching the supplied filters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanTransportChoicePackage : McpPlanTransportChoice +public sealed class EnvironmentsListResult { - /// - [JsonIgnore] - public override string InstallMethod => "package"; - - /// Stable identifier for this choice within the plan, used to select it when the plan is applied. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [MaxLength(128)] - [JsonPropertyName("choiceId")] - public required string ChoiceId { get; set; } - - /// Package identifier. Inert untrusted data. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [MaxLength(512)] - [JsonPropertyName("packageIdentifier")] - public required string PackageIdentifier { get; set; } - - /// Packaging ecosystem, for example `oci` or `npm`. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [MaxLength(64)] - [JsonPropertyName("packageType")] - public required string PackageType { get; set; } - - /// Typed values this choice requires, excluding secrets. - [JsonPropertyName("requiredValues")] - public required IList RequiredValues { get; set; } - - /// Secrets this choice requires, referenced by placeholder only. - [JsonPropertyName("secretPlaceholders")] - public required IList SecretPlaceholders { get; set; } - - /// Local process transport this package choice would use. - [JsonPropertyName("transport")] - public required McpPlanPackageTransport Transport { get; set; } + /// Safe discovery records without relay bootstrap credentials. + [JsonPropertyName("environments")] + public IList Environments { get => field ??= []; set; } } -/// An eligible remote-endpoint transport choice. The endpoint is required and package identity cannot be represented. -/// The remote variant of . +/// Optional discovery filters supported by GitHub Mission Control. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanTransportChoiceRemote : McpPlanTransportChoice +internal sealed class EnvironmentsListRequest { - /// - [JsonIgnore] - public override string InstallMethod => "remote"; - - /// Stable identifier for this choice within the plan, used to select it when the plan is applied. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [MaxLength(128)] - [JsonPropertyName("choiceId")] - public required string ChoiceId { get; set; } + /// Restrict discovery to this compute kind. + [JsonPropertyName("kind")] + public EnvironmentKind? Kind { get; set; } - /// Endpoint URL. Inert untrusted data. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [MaxLength(2048)] - [JsonPropertyName("endpoint")] - public required string Endpoint { get; set; } + /// Operational status, such as online, offline, degraded, waking, or draining. + [JsonPropertyName("status")] + public string? Status { get; set; } +} - /// Typed values this choice requires, excluding secrets. - [JsonPropertyName("requiredValues")] - public required IList RequiredValues { get; set; } +/// Safe discovery information for the requested environment. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class EnvironmentsGetResult +{ + /// The requested environment without relay bootstrap credentials. + [JsonPropertyName("environment")] + public GitHubEnvironment Environment { get => field ??= new(); set; } +} - /// Secrets this choice requires, referenced by placeholder only. - [JsonPropertyName("secretPlaceholders")] - public required IList SecretPlaceholders { get; set; } +/// Identify a Mission Control environment to retrieve. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class EnvironmentsGetRequest +{ + /// Identifier assigned by Mission Control. + [JsonPropertyName("environmentId")] + public string EnvironmentId { get; set; } = string.Empty; +} - /// Endpoint transport this remote choice would use. - [JsonPropertyName("transport")] - public required McpPlanRemoteTransport Transport { get; set; } +/// Acknowledgement that the requested environment was deleted. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class EnvironmentsDeleteResult +{ } -/// A normalised, inert description of what installing an MCP server would involve. Carries no raw card, no install specification, and no secret value. +/// Identify a user-managed Mission Control environment to delete. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpInstallPlan +internal sealed class EnvironmentsDeleteRequest { - /// Alternative configuration changes, with exactly one entry for each transportChoices entry in the same order. Only the entry for the subsequently selected transport applies; these are not cumulative writes. Payloads remain behind the runtime boundary. - [JsonPropertyName("configurationChanges")] - public IList ConfigurationChanges { get => field ??= []; set; } + /// User-managed environment to delete. GitHub-managed environments cannot be deleted. + [JsonPropertyName("environmentId")] + public string EnvironmentId { get; set; } = string.Empty; +} - /// Normalised identity of the server the plan would install. - [JsonPropertyName("identity")] - public McpPlanResourceIdentity Identity { get => field ??= new(); set; } +/// The existing runtime identity and its resource on the listener. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class HostPublishSessionResult +{ + /// Canonical runtime ID of the published session. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Opaque, runtime-instance scoped, TTL-bound, single-use handle for this plan. Rejected when stale, replayed, or presented to a different runtime instance. Never logged. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("planHandle")] - public string PlanHandle { get; set; } = string.Empty; + /// AHP resource URI for the session on this listener. + [JsonPropertyName("sessionUri")] + public string SessionUri { get; set; } = string.Empty; +} - /// ISO 8601 timestamp after which the plan handle is stale and will be rejected. Abandoning a plan needs no call: an unused handle simply expires, so cancellation before commit is side-effect free. - [JsonPropertyName("planHandleExpiresAt")] - public string PlanHandleExpiresAt { get; set; } = string.Empty; +/// Publishes a resident session attached to the listener's owning connection. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class HostPublishSessionRequest +{ + /// Listener UUID returned by host.start. + [JsonPropertyName("hostId")] + public string HostId { get; set; } = string.Empty; - /// Outcome of evaluating the server against registry and enterprise policy. - [JsonPropertyName("policy")] - public McpPlanPolicyResult Policy { get => field ??= new(); set; } + /// Canonical runtime session ID attached to the listener's owning connection. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Origin and semantic digest of the exact validated JSON MCP card content bound to this plan. - [JsonPropertyName("provenance")] - public McpPlanProvenance Provenance { get => field ??= new(); set; } +/// Metadata advertised by the AHP host, not a guarantee that history can be resumed. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class HostSessionSummary +{ + /// Optional activity description advertised by the host. + [JsonPropertyName("activity")] + public string? Activity { get; set; } - /// Identifier of the choice the runtime would pick by default. Omitted when there is no eligible transport, or when the runtime expresses no preference. - [JsonPropertyName("recommendedTransportChoiceId")] - public string? RecommendedTransportChoiceId { get; set; } + /// Session creation time as an ISO 8601 timestamp. + [JsonPropertyName("createdAt")] + public string CreatedAt { get; set; } = string.Empty; - /// Whether applying this plan would require an MCP reload to take effect. Planning itself never reloads. - [JsonPropertyName("reloadRequired")] - public bool ReloadRequired { get; set; } + /// Last modification time as an ISO 8601 timestamp. + [JsonPropertyName("modifiedAt")] + public string ModifiedAt { get; set; } = string.Empty; - /// True only when every eligible transport choice needs additional values or secrets. False means at least one choice needs no additional configuration, not that every choice is ready. A later apply operation must validate the selected choice's own inputs, secrets and policy after explicit confirmation. - [JsonPropertyName("requiresInteractiveConfiguration")] - public bool RequiresInteractiveConfiguration { get; set; } + /// Stable AHP resource URI identifying the session on this host. + [JsonPropertyName("resource")] + public string Resource { get; set; } = string.Empty; - /// Configuration scope and key the plan would write to. - [JsonPropertyName("target")] - public McpPlanTarget Target { get => field ??= new(); set; } + /// Unsigned 32-bit AHP session status bitset (0..=4294967295); unknown bits must be preserved. + [JsonPropertyName("status")] + public long Status { get; set; } - /// Every eligible transport, so a host can present an explicit choice. A completed plan always has at least one; when none is eligible, planning returns `CatalogUnavailableTransportError` instead. - [JsonPropertyName("transportChoices")] - public IList TransportChoices { get => field ??= []; set; } + /// Authoritative title advertised by the host. + [JsonPropertyName("title")] + public string Title { get; set; } = string.Empty; } -/// A computed MCP install plan. Nothing has been applied: the plan describes what installing would change, and the plan handle is what a later apply operation would consume. -/// The planned variant of . +/// The complete live and dormant advertised catalog, without subscribing to sessions. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultPlanned : McpPlanInstallResult +public sealed class HostListSessionsResult { - /// - [JsonIgnore] - public override string Kind => "planned"; - - /// Protocol version and capabilities the runtime honoured. - [JsonPropertyName("negotiated")] - public required CatalogNegotiatedContract Negotiated { get; set; } - - /// The normalised plan. - [JsonPropertyName("plan")] - public required McpInstallPlan Plan { get; set; } + /// Complete advertised session summaries, in the host's catalog order. + [JsonPropertyName("sessions")] + public IList Sessions { get => field ??= []; set; } } -/// The caller's protocol version or required capabilities cannot be honoured. Returned instead of a partial or ambiguous success. -/// The negotiation-refused variant of . +/// Reads a running host's catalog or the runtime's saved default catalog. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultNegotiationRefused : McpPlanInstallResult +internal sealed class HostListSessionsRequest { - /// - [JsonIgnore] - public override string Kind => "negotiation-refused"; - - /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + /// + /// Listener UUID returned by host.start; the caller must own it. Omit to read + /// the saved default compute-identity catalog without starting a listener. + /// + [JsonPropertyName("hostId")] + public string? HostId { get; set; } +} - /// Lowest caller protocol version this runtime will serve. - [JsonPropertyName("minimumSupportedProtocolVersion")] - public required long MinimumSupportedProtocolVersion { get; set; } +/// The resident session the application has materialized on its own connection. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class HostCreateSessionResult +{ + /// Runtime session UUID materialized on the application's SDK connection. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Whether the version or the capability set was the problem. - [JsonPropertyName("reason")] - public required CatalogNegotiationRefusedReason Reason { get; set; } +/// One application-owned session handoff, requested by the supervised hosting participant. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class HostCreateSessionRequest +{ + /// Host-selected SDK creation or resume settings, without executable callbacks or tools. + [JsonPropertyName("config")] + public IDictionary Config { get => field ??= new Dictionary(); set; } - /// Protocol version of the runtime that refused the request. - [JsonPropertyName("runtimeProtocolVersion")] - public required long RuntimeProtocolVersion { get; set; } + /// Unique identity for this participation, independent of the session lifetime. + [JsonPropertyName("handoffId")] + public string HandoffId { get; set; } = string.Empty; - /// Capabilities this runtime can safely advertise to this caller. The complete five-capability protocol-3 legacy set is always present; every capability added after that baseline appears only when the caller required it, so an older closed-enum decoder can still consume a refusal. This list does not imply that every deployment has enabled every operation. - [JsonPropertyName("supportedCapabilities")] - public required IList SupportedCapabilities { get; set; } + /// + /// For an explicit publication, borrow an existing owning-connection resident + /// instead of invoking the resume factory. Cold sessions still use the factory. + /// + [JsonPropertyName("preferResident")] + public bool? PreferResident { get; set; } - /// The subset of the caller's bounded extensible capability identifiers this runtime cannot honour. - [JsonPropertyName("unsupportedCapabilities")] - public required IList UnsupportedCapabilities { get; set; } + /// Resume an app-owned durable session instead of creating a new session. + [JsonPropertyName("resume")] + public bool? Resume { get; set; } } -/// A presented handle was not accepted. Handles are runtime-instance scoped, TTL-bound, and single-use, so each way of failing is reported distinctly. -/// The handle-rejected variant of . +/// Empty acknowledgement for a completed host lifecycle operation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultHandleRejected : McpPlanInstallResult +internal sealed class HostReleaseSessionResult { - /// - [JsonIgnore] - public override string Kind => "handle-rejected"; - - /// Which kind of handle was presented. - [JsonPropertyName("handleType")] - public required CatalogHandleType HandleType { get; set; } - - /// Human-readable explanation, safe to surface. Never contains the handle itself, nor a query, URL, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } +} - /// Why the handle was rejected. - [JsonPropertyName("reason")] - public required CatalogHandleRejectionReason Reason { get; set; } +/// Ends one participation, not the application's session lifetime. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class HostReleaseSessionRequest +{ + /// Identity of the participation to release without destroying the session. + [JsonPropertyName("handoffId")] + public string HandoffId { get; set; } = string.Empty; } -/// The request was rejected because a bounded field fell outside its permitted range or a required field was unusable. Pagination may also be rejected by the authority after a continuation request; repeat the search without page. -/// The invalid-request variant of . +/// Listener readiness, returned only after binding and the supervised participant's SDK handshake. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultInvalidRequest : McpPlanInstallResult +public sealed class HostStartResult { - /// - [JsonIgnore] - public override string Kind => "invalid-request"; + /// GitHub Mission Control environment ID, present when its relay transport is ready. + [JsonPropertyName("environmentId")] + public string? EnvironmentId { get; set; } - /// Which request field was rejected. - [JsonPropertyName("field")] - public required CatalogInvalidRequestField Field { get; set; } + /// Caller-generated listener UUID. + [JsonPropertyName("hostId")] + public string HostId { get; set; } = string.Empty; - /// Human-readable explanation, safe to surface. Never echoes the offending value, nor a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Separate host process ID, when provided by a legacy runtime. Absent for in-process listeners. + [JsonPropertyName("pid")] + public long? Pid { get; set; } + + /// Secret connection token, absent when authentication is disabled. + [JsonPropertyName("token")] + public string? Token { get; set; } + + /// Actual bound WebSocket URL, including the allocated port. + [JsonPropertyName("url")] + public string? Url { get; set; } } -/// An optional catalog authentication exchange did not establish the caller's identity. Anonymous search remains supported; this refusal is reserved for an operation that cannot continue after the attempted exchange. It is distinct from `policy-rejected` and from a network failure, and the reason identifies the recovery action. -/// The authentication-required variant of . +/// GitHub Mission Control registration options. The compute ID is application-owned and stable. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultAuthenticationRequired : McpPlanInstallResult +public sealed class HostGitHubEnvironmentOptions { - /// - [JsonIgnore] - public override string Kind => "authentication-required"; + /// Stable application installation identity, reused across host restarts. + [JsonPropertyName("computeId")] + public string ComputeId { get; set; } = string.Empty; - /// Human-readable explanation, safe to surface. Never contains a credential or token, nor a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Human-readable environment display name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Why authentication failed. Only an expired credential justifies attempting a silent refresh; an absent or rejected credential requires sign-in. - [JsonPropertyName("reason")] - public required CatalogAuthenticationRequiredReason Reason { get; set; } + /// Require sealed, connection-bound authentication (default true), independent of listener address. False permits unsealed direct credentials and unbound sealed relay credentials; use only when the caller fully controls and trusts the direct transport. Relay token encryption remains mandatory. + [JsonPropertyName("requireConnectionBinding")] + public bool? RequireConnectionBinding { get; set; } } -/// Registry or enterprise policy refused the operation. -/// The policy-rejected variant of . +/// Local WebSocket transport options. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultPolicyRejected : McpPlanInstallResult +public sealed class HostLocalServerOptions { - /// - [JsonIgnore] - public override string Kind => "policy-rejected"; + /// Listener hostname. Defaults to 127.0.0.1; explicit non-loopback binds are allowed. + [JsonPropertyName("hostname")] + public string? Hostname { get; set; } - /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Listener port. Omitted or zero requests an OS-allocated port. + [JsonPropertyName("port")] + public int? Port { get; set; } - /// Which authority produced the decision. - [JsonPropertyName("source")] - public required McpPlanPolicySource Source { get; set; } + /// Require token authentication (default true). Cannot be false with a token. + [JsonPropertyName("requireConnectionToken")] + public bool? RequireConnectionToken { get; set; } + + /// Nonempty connection token. Generated randomly when required and omitted. + [JsonPropertyName("token")] + public string? Token { get; set; } } -/// The runtime could not reach the catalog authority or retrieve a card. Covers being offline as well as transport-level failure. -/// The network-failure variant of . +/// Starts a supervised AHP host with at least one explicitly selected transport. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultNetworkFailure : McpPlanInstallResult +public sealed class HostStartRequest { - /// - [JsonIgnore] - public override string Kind => "network-failure"; + /// Stable application identity selecting its durable host catalog. Defaults to the persisted runtime compute GUID; must agree with githubEnvironment.computeId when both are supplied. + [JsonPropertyName("computeId")] + public string? ComputeId { get; set; } - /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Registers a GitHub Mission Control environment and enables its relay transport. + [JsonPropertyName("githubEnvironment")] + public HostGitHubEnvironmentOptions? GitHubEnvironment { get; set; } - /// Categorised failure, low cardinality so it can be aggregated without carrying a URL. - [JsonPropertyName("reason")] - public required CatalogNetworkFailureReason Reason { get; set; } + /// Caller-generated UUID identifying this connection-owned listener. + [JsonPropertyName("hostId")] + public required string HostId { get; set; } - /// Bounded cooldown in seconds before another catalog request should be attempted, when the authority supplied a numeric Retry-After value or the runtime applied its documented fallback. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("retryAfterSeconds")] - public int? RetryAfterSeconds { get; set; } + /// Enables a local WebSocket listener. + [JsonPropertyName("localServer")] + public HostLocalServerOptions? LocalServer { get; set; } - /// HTTP status code, when the failure was a rejected response. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("statusCode")] - public int? StatusCode { get; set; } + /// Ask the owning application to resume its durable AHP sessions. + [JsonPropertyName("resumeFactory")] + public bool? ResumeFactory { get; set; } + + /// Ask the owning SDK application to materialize AHP sessions. + [JsonPropertyName("sessionFactory")] + public bool? SessionFactory { get; set; } } -/// Retrieval was refused by the runtime's hardened fetch boundary before any request left the process, or before a redirect was followed. -/// The unsafe-retrieval variant of . +/// Empty acknowledgement for a completed host lifecycle operation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultUnsafeRetrieval : McpPlanInstallResult +public sealed class HostDisposeResult { - /// - [JsonIgnore] - public override string Kind => "unsafe-retrieval"; - - /// Human-readable explanation, safe to surface. Never contains the refused URL, nor a query, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } +} - /// Which control refused the retrieval, low cardinality so it can be aggregated without carrying a URL. - [JsonPropertyName("reason")] - public required CatalogUnsafeRetrievalReason Reason { get; set; } +/// Stops a connection-owned listener and joins its teardown. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class HostDisposeRequest +{ + /// Listener UUID. Unknown or successfully stopped IDs are harmless. + [JsonPropertyName("hostId")] + public string HostId { get; set; } = string.Empty; } -/// A card could not be parsed or did not satisfy its declared media type's schema. -/// The malformed-card variant of . +/// Private credentials delivered only to a runtime-owned Mission Control hosting participant. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultMalformedCard : McpPlanInstallResult +internal sealed class HostGetEnvironmentCredentialsResult { - /// - [JsonIgnore] - public override string Kind => "malformed-card"; + /// GitHub API base URL for the authenticated service. + [JsonPropertyName("githubApiUrl")] + public string GitHubApiUrl { get; set; } = string.Empty; - /// Media type the card was interpreted as, when it declared one this runtime recognises. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("mediaType")] - public CatalogMediaType? MediaType { get; set; } + /// Hostname of the authenticated GitHub service. + [JsonPropertyName("githubHost")] + public string GitHubHost { get; set; } = string.Empty; - /// Human-readable explanation, safe to surface. Never echoes card content, nor a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Mission Control API origin for environment registration and management. + [JsonPropertyName("missionControlUrl")] + public string MissionControlUrl { get; set; } = string.Empty; - /// How the card failed validation. - [JsonPropertyName("reason")] - public required CatalogMalformedCardReason Reason { get; set; } + /// Current bearer token for the authenticated GitHub identity. + [JsonPropertyName("token")] + public string Token { get; set; } = string.Empty; } -/// An upstream catalog response broke the wire contract. Most importantly, every result must carry exactly one of a URL or embedded data: a result carrying both, or neither, is refused here rather than being guessed at. -/// The contract-violation variant of . +/// Normalized local WebSocket listener settings. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultContractViolation : McpPlanInstallResult +public sealed class HostLocalServerConfiguration { - /// - [JsonIgnore] - public override string Kind => "contract-violation"; + /// Hostname or IP address to bind. + [JsonPropertyName("hostname")] + public string Hostname { get; set; } = string.Empty; - /// Human-readable explanation, safe to surface. Never echoes response content, nor a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Port to bind, with zero requesting OS allocation. + [JsonPropertyName("port")] + public int Port { get; set; } - /// Which rule the response broke. - [JsonPropertyName("reason")] - public required CatalogContractViolationReason Reason { get; set; } + /// Whether the listener requires token authentication. + [JsonPropertyName("requireConnectionToken")] + public bool RequireConnectionToken { get; set; } + + /// Secret connection token, absent when authentication is disabled. + [JsonPropertyName("token")] + public string? Token { get; set; } } -/// No transport this runtime can use is available for the requested server. -/// The unavailable-transport variant of . +/// Normalized listener settings delivered only to the supervised hosting participant. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultUnavailableTransport : McpPlanInstallResult +internal sealed class HostGetConfigurationResult { - /// - [JsonIgnore] - public override string Kind => "unavailable-transport"; + /// Requested GitHub Mission Control registration. + [JsonPropertyName("githubEnvironment")] + public HostGitHubEnvironmentOptions? GitHubEnvironment { get; set; } - /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Normalized local listener settings, absent for relay-only hosts. + [JsonPropertyName("localServer")] + public HostLocalServerConfiguration? LocalServer { get; set; } - /// Why no transport could be offered. - [JsonPropertyName("reason")] - public required CatalogUnavailableTransportReason Reason { get; set; } + /// Whether app-owned durable sessions are resumed by the owning application. + [JsonPropertyName("resumeFactory")] + public bool ResumeFactory { get; set; } + + /// Whether session materialization is delegated to the owning application. + [JsonPropertyName("sessionFactory")] + public bool SessionFactory { get; set; } } -/// The candidate is discoverable but cannot be installed. `application/ai-skill` resolves here, because it stays searchable while remaining typed non-installable. -/// The not-installable variant of . +/// Empty acknowledgement for a completed host lifecycle operation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultNotInstallable : McpPlanInstallResult +internal sealed class HostReadyResult { - /// - [JsonIgnore] - public override string Kind => "not-installable"; +} - /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } +/// Readiness reported by the supervised hosting participant on its own SDK connection. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class HostReadyRequest +{ + /// Actual bound WebSocket URL. + [JsonPropertyName("address")] + public string? Address { get; set; } - /// Why the candidate cannot be installed. - [JsonPropertyName("reason")] - public required CatalogNotInstallableReason Reason { get; set; } + /// Registered environment ID, reported only once the relay transport is connected. + [JsonPropertyName("environmentId")] + public string? EnvironmentId { get; set; } + + /// Configured secret token, absent when authentication is disabled. + [JsonPropertyName("token")] + public string? Token { get; set; } } -/// The operation is not available on this runtime. Distinct from a network failure: nothing was attempted. -/// The unavailable variant of . +/// One server-discovered hook action from user, repository, plugin, or managed-policy configuration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultUnavailable : McpPlanInstallResult +public sealed class DiscoveredHook { - /// - [JsonIgnore] - public override string Kind => "unavailable"; + /// Durable content hash used by hook enablement. Identical actions may intentionally share this key. Omitted when changing the user's disabled-hooks setting cannot change the action's current server-discovered state, including managed-policy hooks, session-start prompt actions, actions suppressed by disable-all settings, and projectless plugin actions that require project-directory expansion. + [JsonPropertyName("disableKey")] + public string? DisableKey { get; set; } - /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Whether this action is enabled under the server-side discovery settings. Concrete sessions may differ because they can add session-specific directories, plugins, or trust. False when its disable key is present in the user's disabled-hooks setting or disable-all settings suppress the action. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } - /// Why the operation is unavailable. - [JsonPropertyName("reason")] - public required CatalogUnavailableReason Reason { get; set; } + /// Hook event that invokes this action. + [JsonPropertyName("hookType")] + public HookType HookType { get; set; } + + /// Deterministic identifier for this server-discovered action row. It remains stable while the project, origin, source, event, action content, and duplicate ordinal are unchanged. This is row identity, not the key persisted in disabledHooks. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Configuration tier that contributed this hook action. + [JsonPropertyName("origin")] + public HookOrigin Origin { get; set; } + + /// Input project path for which this server-side action was resolved. Set on every row returned for project-scoped discovery, including repeated user and policy actions. + [JsonPropertyName("projectPath")] + public string? ProjectPath { get; set; } + + /// Human-readable source label, such as a hook file path, settings source, or plugin name. + [JsonPropertyName("source")] + public string? Source { get; set; } } -/// The protocol version and capability set a caller requires, supplied on every catalog request so negotiation cannot be skipped by omission. +/// Server-discovered hook actions and partial-load diagnostics from user, repository, plugin, and managed-policy sources. Concrete sessions may include additional session-specific hook sources. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CatalogClientContract +public sealed class HooksDiscoverResult { - /// SDK protocol version the caller was generated against. A caller below the runtime's minimum supported version is refused rather than served a partial result. - [JsonPropertyName("protocolVersion")] - public long ProtocolVersion { get; set; } + /// Errors for hook sources or actions that could not be loaded, making the result partially incomplete. Other valid actions are still returned. Project-resolution and repository-settings errors are prefixed with their project path. + [JsonPropertyName("errors")] + public IList Errors { get => field ??= []; set; } - /// Wire features the caller requires the runtime to understand. Identifiers are bounded but extensible so a newer caller can negotiate with an older runtime. Requiring an unknown feature yields a typed refusal listing what is understood, never a partial grant. A grant does not promise that a deployment has enabled the operation; typed unavailable results report that separately. - [JsonPropertyName("requiredCapabilities")] - public IList RequiredCapabilities { get => field ??= []; set; } + /// All discovered hook actions. Byte-identical actions remain separate rows even when they share a disable key. + [JsonPropertyName("hooks")] + public IList Hooks { get => field ??= []; set; } + + /// Non-fatal source-loading warnings. Discovery remains complete for the affected source, although the source had a recoverable issue. Repository-settings warnings are prefixed with their project path when attribution is available. + [JsonPropertyName("warnings")] + public IList Warnings { get => field ??= []; set; } } -/// What an install plan is computed from: a candidate handle from a previous search, or a card supplied directly. -/// Polymorphic base type discriminated by kind. +/// Optional project paths and host-exclusion behavior for server-scoped hook discovery. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(McpPlanInstallSourceCandidate), "candidate")] -[JsonDerivedType(typeof(McpPlanInstallSourceCard), "card")] -public partial class McpPlanInstallSource +public sealed class HooksDiscoverRequest { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// When true, omit host-owned user and plugin hook rows and their diagnostics. Managed-policy hooks and trusted repository hooks remain visible, and host disabledHooks still contribute to each remaining row's effective enabled state. This filters sources rather than simulating a host with no settings. + [JsonPropertyName("excludeHostHooks")] + public bool? ExcludeHostHooks { get; set; } + /// Explicit discovery-only file-hook roots, separate from callback hooks and filesystem grants. Available without ambient discovery or repository file-hook enablement, subject to managed/hook disables. Relative paths resolve against the request's effective working directory when supplied. Null and omission are equivalent; an empty array contributes no explicit roots. + [JsonPropertyName("hookDirectories")] + public IList? HookDirectories { get; set; } -/// Plan from a candidate returned by a previous catalog search. -/// The candidate variant of . + /// Optional project directory paths whose trusted repository and project-expanded plugin hooks should be discovered. When omitted or empty, user, managed-policy, and globally enabled installed or explicit plugin hooks are returned without project expansion. + [JsonPropertyName("projectPaths")] + public IList? ProjectPaths { get; set; } +} + +/// Active server-driven promotion for a model, including its discount and optional expiry. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallSourceCandidate : McpPlanInstallSource +public sealed class ModelBillingPromo { - /// - [JsonIgnore] - public override string Kind => "candidate"; + /// Percentage discount (0-100) applied while the promotion is active. May be fractional. + [JsonPropertyName("discountPercent")] + public double? DiscountPercent { get; set; } - /// Single-use candidate handle. Consumed by this call, so a replay of the same handle is rejected. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [MaxLength(256)] - [JsonPropertyName("candidateHandle")] - public required string CandidateHandle { get; set; } + /// UTC ISO 8601 timestamp marking when the promotion ends. Optional: an open-ended promotion omits this field. When present, the API only surfaces a promo whose expiry parses and is in the future, so consumers should treat a past value as expired. + [JsonPropertyName("endsAt")] + public string? EndsAt { get; set; } - /// The runtime- or authority-minted `searchId` returned with the search that produced this candidate. Planning verifies the private correlation and atomically consumes a matching candidate before downstream work, including attempts that subsequently fail or report unavailable. A mismatched search does not consume the candidate. It identifies a search rather than a person and must never be joined with user identity to re-identify anyone. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [MaxLength(64)] - [JsonPropertyName("searchId")] - public required string SearchId { get; set; } + /// Stable identifier for the promotion campaign. + [JsonPropertyName("id")] + public string? Id { get; set; } + + /// Human-readable promotion message. Does not include the expiry timestamp; consumers may format endsAt and append it when present. + [JsonPropertyName("message")] + public string? Message { get; set; } + + /// Whether the service asked hosts to give this promotion a prominent surface, such as a dedicated banner, in addition to listing it with the model. `true` requests that surface and `false` asks for the model list only. Absent means the service expressed no preference — for example a response that predates the field — so hosts should apply their own default rather than read it as `false`. + [JsonPropertyName("showBanner")] + public bool? ShowBanner { get; set; } } -/// A card supplied directly by the caller. Exactly one of a URL or embedded data, encoded structurally so neither both nor neither can be expressed. -/// Polymorphic base type discriminated by kind. +/// Long context tier pricing (available for models with extended context windows). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(McpServerCardReferenceUrl), "url")] -[JsonDerivedType(typeof(McpServerCardReferenceEmbedded), "embedded")] -public partial class McpServerCardReference +public sealed class ModelBillingTokenPricesLongContext { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// Use cacheReadPrice instead. AI Credits cost per billing batch of cached tokens. + [EditorBrowsable(EditorBrowsableState.Never)] +#if NET5_0_OR_GREATER + [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] +#else + [Obsolete("This member is deprecated and will be removed in a future version.")] +#endif + [JsonPropertyName("cachePrice")] + public double? CachePrice { get; set; } + /// AI Credits cost per billing batch of cached (read) tokens. + [JsonPropertyName("cacheReadPrice")] + public double? CacheReadPrice { get; set; } -/// An MCP server card to be retrieved from a URL through the runtime's hardened fetch boundary. -/// The url variant of . + /// AI Credits cost per billing batch of 1-hour cache-write (cache creation) tokens. + [JsonPropertyName("cacheWrite1hPrice")] + public double? CacheWrite1hPrice { get; set; } + + /// AI Credits cost per billing batch of cache-write (cache creation) tokens. + [JsonPropertyName("cacheWritePrice")] + public double? CacheWritePrice { get; set; } + + /// Use maxPromptTokens instead. Prompt token budget for the long context tier. The total context window is this value plus the model's max_output_tokens. + [EditorBrowsable(EditorBrowsableState.Never)] +#if NET5_0_OR_GREATER + [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] +#else + [Obsolete("This member is deprecated and will be removed in a future version.")] +#endif + [JsonPropertyName("contextMax")] + public long? ContextMax { get; set; } + + /// AI Credits cost per billing batch of input tokens. + [JsonPropertyName("inputPrice")] + public double? InputPrice { get; set; } + + /// Prompt token budget for the long context tier. The total context window is this value plus the model's max_output_tokens. + [JsonPropertyName("maxPromptTokens")] + public long? MaxPromptTokens { get; set; } + + /// AI Credits cost per billing batch of output tokens. + [JsonPropertyName("outputPrice")] + public double? OutputPrice { get; set; } +} + +/// Token-level pricing information for this model. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpServerCardReferenceUrl : McpServerCardReference +public sealed class ModelBillingTokenPrices { - /// - [JsonIgnore] - public override string Kind => "url"; + /// Number of tokens per standard billing batch. + [JsonPropertyName("batchSize")] + public long? BatchSize { get; set; } - /// Media type the card is expected to conform to. - [JsonPropertyName("mediaType")] - public required McpServerCardMediaType MediaType { get; set; } + /// Use cacheReadPrice instead. AI Credits cost per billing batch of cached tokens. + [EditorBrowsable(EditorBrowsableState.Never)] +#if NET5_0_OR_GREATER + [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] +#else + [Obsolete("This member is deprecated and will be removed in a future version.")] +#endif + [JsonPropertyName("cachePrice")] + public double? CachePrice { get; set; } - /// Card URL. Retrieved only through the runtime's hardened boundary, with scheme, credential, address-range, redirect, timeout, and response-size controls applied. Never logged. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [MaxLength(2048)] - [JsonPropertyName("url")] - public required string Url { get; set; } + /// AI Credits cost per billing batch of cached (read) tokens. + [JsonPropertyName("cacheReadPrice")] + public double? CacheReadPrice { get; set; } + + /// AI Credits cost per billing batch of 1-hour cache-write (cache creation) tokens. + [JsonPropertyName("cacheWrite1hPrice")] + public double? CacheWrite1hPrice { get; set; } + + /// AI Credits cost per billing batch of cache-write (cache creation) tokens. + [JsonPropertyName("cacheWritePrice")] + public double? CacheWritePrice { get; set; } + + /// Use maxPromptTokens instead. Prompt token budget for the default tier. The total context window is this value plus the model's max_output_tokens. + [EditorBrowsable(EditorBrowsableState.Never)] +#if NET5_0_OR_GREATER + [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] +#else + [Obsolete("This member is deprecated and will be removed in a future version.")] +#endif + [JsonPropertyName("contextMax")] + public long? ContextMax { get; set; } + + /// AI Credits cost per billing batch of input tokens. + [JsonPropertyName("inputPrice")] + public double? InputPrice { get; set; } + + /// Long context tier pricing (available for models with extended context windows). + [JsonPropertyName("longContext")] + public ModelBillingTokenPricesLongContext? LongContext { get; set; } + + /// Prompt token budget for the default tier. The total context window is this value plus the model's max_output_tokens. + [JsonPropertyName("maxPromptTokens")] + public long? MaxPromptTokens { get; set; } + + /// AI Credits cost per billing batch of output tokens. + [JsonPropertyName("outputPrice")] + public double? OutputPrice { get; set; } } -/// An MCP server card supplied inline as an inert document. -/// The embedded variant of . +/// Billing information. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpServerCardReferenceEmbedded : McpServerCardReference +public sealed class ModelBilling { - /// - [JsonIgnore] - public override string Kind => "embedded"; + /// Whole-number percentage discount (0-100) applied to usage billed through this model. Populated for the synthetic `auto` model, where requests routed by auto-mode are billed at a reduced rate; absent for concrete models. + [JsonPropertyName("discountPercent")] + public int? DiscountPercent { get; set; } - /// The card document verbatim, treated as inert untrusted bytes. The runtime parses and validates it; the host is not expected to interpret it. Never logged. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [MaxLength(1048576)] - [JsonPropertyName("data")] - public required string Data { get; set; } + /// Billing cost multiplier relative to the base rate. + [JsonPropertyName("multiplier")] + public double? Multiplier { get; set; } - /// Media type the card is expected to conform to. - [JsonPropertyName("mediaType")] - public required McpServerCardMediaType MediaType { get; set; } + /// Active server-driven promotion for this model, if any. Present when the model is being promoted with a discount, which may be time-boxed or open-ended. + [JsonPropertyName("promo")] + public ModelBillingPromo? Promo { get; set; } + + /// Token-level pricing information for this model. + [JsonPropertyName("tokenPrices")] + public ModelBillingTokenPrices? TokenPrices { get; set; } } -/// Plan from a card supplied directly by the caller, without a preceding search. -/// The card variant of . +/// Vision-specific limits. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallSourceCard : McpPlanInstallSource +public sealed class ModelCapabilitiesLimitsVision { - /// - [JsonIgnore] - public override string Kind => "card"; + /// Maximum image size in bytes. + [JsonPropertyName("max_prompt_image_size")] + public long MaxPromptImageSize { get; set; } - /// The card to plan from: exactly one of a URL or embedded data. - [JsonPropertyName("card")] - public required McpServerCardReference Card { get; set; } + /// Maximum number of images per prompt. + [JsonPropertyName("max_prompt_images")] + public long MaxPromptImages { get; set; } + + /// MIME types the model accepts. + [JsonPropertyName("supported_media_types")] + public IList SupportedMediaTypes { get => field ??= []; set; } } -/// A side-effect-free request for an MCP install plan. Computing a plan never writes configuration, stores a secret, or reloads MCP servers. +/// Token limits for prompts, outputs, and context window. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpPlanInstallRequest +public sealed class ModelCapabilitiesLimits { - /// Protocol version and capabilities the caller requires. - [JsonPropertyName("contract")] - public required CatalogClientContract Contract { get; set; } + /// Maximum total context window size in tokens. + [JsonPropertyName("max_context_window_tokens")] + public long? MaxContextWindowTokens { get; set; } - /// The same existing attached session that owns the original catalogue candidate. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [MaxLength(256)] - [JsonPropertyName("policySessionId")] - public string? PolicySessionId { get; set; } + /// Maximum number of output/completion tokens. + [JsonPropertyName("max_output_tokens")] + public long? MaxOutputTokens { get; set; } - /// Configuration scope the plan targets. Defaults to user scope when omitted. - [JsonPropertyName("scope")] - public McpPlanScope? Scope { get; set; } + /// Maximum number of prompt/input tokens. + [JsonPropertyName("max_prompt_tokens")] + public long? MaxPromptTokens { get; set; } - /// What to plan: either a candidate handle from a previous search, or a card supplied directly. - [JsonPropertyName("source")] - public required McpPlanInstallSource Source { get; set; } + /// Vision-specific limits. + [JsonPropertyName("vision")] + public ModelCapabilitiesLimitsVision? Vision { get; set; } } -/// Management result with contract receipt, or a typed request/negotiation refusal. -/// Polymorphic base type discriminated by kind. +/// Feature flags indicating what the model supports. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(McpInstallationManagementResultOutcome), "outcome")] -[JsonDerivedType(typeof(McpInstallationManagementResultNegotiationRefused), "negotiation-refused")] -[JsonDerivedType(typeof(McpInstallationManagementResultInvalidRequest), "invalid-request")] -public partial class McpInstallationManagementResult +public sealed class ModelCapabilitiesSupports { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// Resolved Anthropic adaptive-thinking capability — unsupported / optional / required / adaptive_only. 'required' models reject thinking.type='enabled' with HTTP 400 but still accept 'disabled' (e.g. opus-4.7/4.8/5, sonnet-5); 'adaptive_only' models accept nothing but 'adaptive' (e.g. fable, mythos). + [JsonPropertyName("adaptive_thinking")] + public AdaptiveThinkingSupport? AdaptiveThinking { get; set; } + /// Whether this model supports reasoning effort configuration. + [JsonPropertyName("reasoningEffort")] + public bool? ReasoningEffort { get; set; } -/// Read-only or recovery management result, never permission to activate or replay. -/// Polymorphic base type discriminated by kind. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(McpInstallationManagementOutcomeRecoveryRequired), "recovery-required")] -[JsonDerivedType(typeof(McpInstallationManagementOutcomeInstallPrepared), "install-prepared")] -[JsonDerivedType(typeof(McpInstallationManagementOutcomeListed), "listed")] -[JsonDerivedType(typeof(McpInstallationManagementOutcomeRecovered), "recovered")] -[JsonDerivedType(typeof(McpInstallationManagementOutcomeUninstallPlanned), "uninstall-planned")] -[JsonDerivedType(typeof(McpInstallationManagementOutcomeOperation), "operation")] -[JsonDerivedType(typeof(McpInstallationManagementOutcomeRefused), "refused")] -public partial class McpInstallationManagementOutcome -{ - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// Whether the model supports provider-native thinking. Independent of configurable reasoning effort; omission means unknown. + [JsonPropertyName("thinking")] + public bool? Thinking { get; set; } + /// Whether this model supports canonical tool calling. + [JsonPropertyName("toolCalls")] + public bool? ToolCalls { get; set; } -/// Already-confirmed durable work must be reconciled before new mutations or inventory. -/// The recovery-required variant of . + /// Whether this model supports vision/image input. + [JsonPropertyName("vision")] + public bool? Vision { get; set; } +} + +/// Model capabilities and limits. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationManagementOutcomeRecoveryRequired : McpInstallationManagementOutcome +public sealed class ModelCapabilities { - /// - [JsonIgnore] - public override string Kind => "recovery-required"; + /// Token limits for prompts, outputs, and context window. + [JsonPropertyName("limits")] + public ModelCapabilitiesLimits? Limits { get; set; } + + /// Feature flags indicating what the model supports. + [JsonPropertyName("supports")] + public ModelCapabilitiesSupports? Supports { get; set; } } -/// Inert, runtime-owned admission. The operation ID is known before confirmation or effects. +/// A service-published message about a model, carrying a stable machine-readable code alongside human-readable text. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpPreparedInstall +public sealed class ModelMessage { - /// Original plan expiry in Unix epoch milliseconds; preparation does not extend it. - [JsonPropertyName("expiresAtEpochMs")] - public long ExpiresAtEpochMs { get; set; } + /// Stable machine-readable identifier for the message, such as `client_version_deprecated`. Hosts can key custom presentation off this; unrecognized codes should fall back to displaying `message`. + [JsonPropertyName("code")] + public string Code { get; set; } = string.Empty; - /// Original connection-owned operation, known before the first confirmation callback. - [JsonPropertyName("operationId")] - public string OperationId { get; set; } = string.Empty; + /// Human-readable message text intended for display to the user. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; } -/// The install-prepared variant of . +/// Policy state (if applicable). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationManagementOutcomeInstallPrepared : McpInstallationManagementOutcome +public sealed class ModelPolicy { - /// - [JsonIgnore] - public override string Kind => "install-prepared"; + /// Current policy state for this model. + [JsonPropertyName("state")] + public ModelPolicyState State { get; set; } - /// Known original operation identity, returned before callback or effects. - [JsonPropertyName("operation")] - public required McpPreparedInstall Operation { get; set; } + /// Usage terms or conditions for this model. + [JsonPropertyName("terms")] + public string? Terms { get; set; } } -/// A versioned, bounded trust observation carried unchanged with a catalog candidate and its private handle context. Current observations require a recognised T1/T2 tier; every non-current state structurally forbids a tier. Eligibility remains `unknown` while Agent Finder supplies no exposure decision, and states absent from its current wire are never inferred from age, relevance, popularity, or a tier transition. -/// Polymorphic base type discriminated by status. +/// Service-published warning text that hosts should display when presenting a model. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "status", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(CatalogTrustSnapshotCurrent), "current")] -[JsonDerivedType(typeof(CatalogTrustSnapshotAbsent), "absent")] -[JsonDerivedType(typeof(CatalogTrustSnapshotStale), "stale")] -[JsonDerivedType(typeof(CatalogTrustSnapshotDowngraded), "downgraded")] -[JsonDerivedType(typeof(CatalogTrustSnapshotRevoked), "revoked")] -[JsonDerivedType(typeof(CatalogTrustSnapshotUnsupported), "unsupported")] -[JsonDerivedType(typeof(CatalogTrustSnapshotMalformed), "malformed")] -public partial class CatalogTrustSnapshot +public sealed class ModelWarningText { - /// The type discriminator. - [JsonPropertyName("status")] - public virtual string Status { get; set; } = string.Empty; + /// Data-retention warning for the model. The text may contain Markdown links and should be rendered as Markdown when supported. + [JsonPropertyName("dataRetention")] + public string? DataRetention { get; set; } } - -/// Where and when the runtime observed the trust metadata. Observation time is not the authority's evaluation time and must not be used to infer staleness. +/// Copilot model metadata, including identifier, display name, capabilities, policy, billing, reasoning efforts, and picker categories. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CatalogTrustProvenance +public sealed class Model { - /// ISO 8601 timestamp with a timezone offset at which the runtime observed the search result carrying this trust field. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(20)] - [MaxLength(64)] - [JsonPropertyName("observedAt")] - public DateTimeOffset ObservedAt { get; set; } + /// Billing information. + [JsonPropertyName("billing")] + public ModelBilling? Billing { get; set; } - /// Bounded authority that supplied the trust field. - [JsonPropertyName("source")] - public CatalogTrustSource Source { get; set; } -} + /// Model capabilities and limits. + [JsonPropertyName("capabilities")] + public ModelCapabilities Capabilities { get => field ??= new(); set; } -/// A recognised current Agent Finder T1 or T2 trust tier. -/// The current variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogTrustSnapshotCurrent : CatalogTrustSnapshot -{ - /// - [JsonIgnore] - public override string Status => "current"; + /// Default reasoning effort level (only present if model supports reasoning effort). + [JsonPropertyName("defaultReasoningEffort")] + public string? DefaultReasoningEffort { get; set; } - /// Service-computed exposure eligibility. `unknown` is required while Agent Finder returns no explicit eligibility field. - [JsonPropertyName("eligibility")] - public required CatalogTrustEligibility Eligibility { get; set; } + /// Model identifier (e.g., "claude-sonnet-4.5"). + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// Bounded source and observation time for this snapshot. This is distinct from evidence used by the authority to calculate trust. - [JsonPropertyName("provenance")] - public required CatalogTrustProvenance Provenance { get; set; } + /// Informational notices the service published for this model, such as an upcoming change or a recommended alternative. Present only when the service published at least one notice. Hosts should surface these without implying anything is wrong with the model. + [JsonPropertyName("infoMessages")] + public IList? InfoMessages { get; set; } - /// Schema version of this runtime-owned snapshot envelope. - [JsonPropertyName("schemaVersion")] - public required CatalogTrustSnapshotSchemaVersion SchemaVersion { get; set; } + /// Provider-supplied model metadata. Keys and JSON-compatible values are preserved unchanged. This is factual metadata published by the model provider; it carries no picker or UX semantics. + [JsonPropertyName("metadata")] + public IDictionary? Metadata { get; set; } - /// Service-computed T1 or T2 trust tier. - [JsonPropertyName("tier")] - public required CatalogTrustTier Tier { get; set; } -} + /// Model capability category for grouping in the model picker. + [JsonPropertyName("modelPickerCategory")] + public ModelPickerCategory? ModelPickerCategory { get; set; } -/// Discriminator: the authority omitted trust metadata. -/// The absent variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogTrustSnapshotAbsent : CatalogTrustSnapshot -{ - /// - [JsonIgnore] - public override string Status => "absent"; + /// Relative cost tier for token-based billing users. + [JsonPropertyName("modelPickerPriceCategory")] + public ModelPickerPriceCategory? ModelPickerPriceCategory { get; set; } - /// Service-computed exposure eligibility. `unknown` is required while Agent Finder returns no explicit eligibility field. - [JsonPropertyName("eligibility")] - public required CatalogTrustEligibility Eligibility { get; set; } + /// Display name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Bounded source and observation time for this snapshot. This is distinct from evidence used by the authority to calculate trust. - [JsonPropertyName("provenance")] - public required CatalogTrustProvenance Provenance { get; set; } + /// Policy state (if applicable). + [JsonPropertyName("policy")] + public ModelPolicy? Policy { get; set; } - /// Schema version of this runtime-owned snapshot envelope. - [JsonPropertyName("schemaVersion")] - public required CatalogTrustSnapshotSchemaVersion SchemaVersion { get; set; } -} + /// The model provider that produced this model, as a neutral reference (opaque id, human-readable label, and provider kind). Present on models returned by `session.model.list`, which resolves provider attribution from the session's account roster; absent on the flat `server.models.list`, which does not resolve a session roster. The model picker groups by this reference. + [JsonPropertyName("provider")] + public ModelProviderRef? Provider { get; set; } -/// Discriminator: the authority explicitly marked the assessment stale. -/// The stale variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogTrustSnapshotStale : CatalogTrustSnapshot -{ - /// - [JsonIgnore] - public override string Status => "stale"; + /// Context-window tiers this model offers, when the provider advertises them independently of tiered token pricing. Copilot models carry their tiers in `billing.tokenPrices`; a provider that has no pricing to publish (an agent host reached over AHP, for example) declares them here instead, so the model picker can still offer the tier toggle. + [JsonPropertyName("supportedContextTiers")] + public IList? SupportedContextTiers { get; set; } - /// Service-computed exposure eligibility. `unknown` is required while Agent Finder returns no explicit eligibility field. - [JsonPropertyName("eligibility")] - public required CatalogTrustEligibility Eligibility { get; set; } + /// Supported reasoning effort levels (only present if model supports reasoning effort). + [JsonPropertyName("supportedReasoningEfforts")] + public IList? SupportedReasoningEfforts { get; set; } - /// Bounded source and observation time for this snapshot. This is distinct from evidence used by the authority to calculate trust. - [JsonPropertyName("provenance")] - public required CatalogTrustProvenance Provenance { get; set; } + /// Model vendor as the Copilot API reports it, for example "Anthropic" or "Azure OpenAI". Open vocabulary, passed through unchanged. It can name the vendor that serves the model instead of the one that built it, or a label that is not a vendor, such as "Experimental". Absent when the Copilot API reports no vendor. + [JsonPropertyName("vendor")] + public string? Vendor { get; set; } - /// Schema version of this runtime-owned snapshot envelope. - [JsonPropertyName("schemaVersion")] - public required CatalogTrustSnapshotSchemaVersion SchemaVersion { get; set; } + /// Warnings the service published for this model, such as a deprecated client version. Present only when the service published at least one warning. The model remains usable; hosts should surface these as advisory rather than blocking. + [JsonPropertyName("warningMessages")] + public IList? WarningMessages { get; set; } + + /// Warning text the service requires hosts to surface for this model. Present only when the service published at least one warning. + [JsonPropertyName("warningText")] + public ModelWarningText? WarningText { get; set; } } -/// Discriminator: the authority explicitly reported a downgraded assessment. -/// The downgraded variant of . +/// List of Copilot models available to the resolved user, including capabilities and billing metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogTrustSnapshotDowngraded : CatalogTrustSnapshot +public sealed class ModelList { - /// - [JsonIgnore] - public override string Status => "downgraded"; - - /// Service-computed exposure eligibility. `unknown` is required while Agent Finder returns no explicit eligibility field. - [JsonPropertyName("eligibility")] - public required CatalogTrustEligibility Eligibility { get; set; } - - /// Bounded source and observation time for this snapshot. This is distinct from evidence used by the authority to calculate trust. - [JsonPropertyName("provenance")] - public required CatalogTrustProvenance Provenance { get; set; } - - /// Schema version of this runtime-owned snapshot envelope. - [JsonPropertyName("schemaVersion")] - public required CatalogTrustSnapshotSchemaVersion SchemaVersion { get; set; } + /// List of available models with full metadata. + [JsonPropertyName("models")] + public IList Models { get => field ??= []; set; } } -/// Discriminator: the authority explicitly revoked the assessment. -/// The revoked variant of . +/// RPC data type for ModelsList operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogTrustSnapshotRevoked : CatalogTrustSnapshot +internal sealed class ModelsListRequest { - /// - [JsonIgnore] - public override string Status => "revoked"; - - /// Service-computed exposure eligibility. `unknown` is required while Agent Finder returns no explicit eligibility field. - [JsonPropertyName("eligibility")] - public required CatalogTrustEligibility Eligibility { get; set; } - - /// Bounded source and observation time for this snapshot. This is distinct from evidence used by the authority to calculate trust. - [JsonPropertyName("provenance")] - public required CatalogTrustProvenance Provenance { get; set; } + /// GitHub token accepted for compatibility with existing SDK clients. When provided, resolves this token instead of using the current account. + [JsonPropertyName("gitHubToken")] + public string? GitHubToken { get; set; } - /// Schema version of this runtime-owned snapshot envelope. - [JsonPropertyName("schemaVersion")] - public required CatalogTrustSnapshotSchemaVersion SchemaVersion { get; set; } + /// Opaque account identifier returned by `account.getAllUsers`. When omitted, the current account is used. + [JsonPropertyName("selectionId")] + public string? SelectionId { get; set; } } -/// Discriminator: the authority supplied a bounded trust value this runtime does not understand. -/// The unsupported variant of . +/// A well-known model in the runtime's built-in catalog. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogTrustSnapshotUnsupported : CatalogTrustSnapshot +public sealed class BuiltInModelCatalogEntry { - /// - [JsonIgnore] - public override string Status => "unsupported"; - - /// Service-computed exposure eligibility. `unknown` is required while Agent Finder returns no explicit eligibility field. - [JsonPropertyName("eligibility")] - public required CatalogTrustEligibility Eligibility { get; set; } - - /// Bounded source and observation time for this snapshot. This is distinct from evidence used by the authority to calculate trust. - [JsonPropertyName("provenance")] - public required CatalogTrustProvenance Provenance { get; set; } - - /// Schema version of this runtime-owned snapshot envelope. - [JsonPropertyName("schemaVersion")] - public required CatalogTrustSnapshotSchemaVersion SchemaVersion { get; set; } + /// Well-known runtime model ID suitable for provider or provider-model metadata. This is not necessarily the provider-facing deployment or model name and does not indicate CAPI entitlement or provider availability. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; } -/// Discriminator: the trust field was empty, unbounded, or had the wrong JSON type. -/// The malformed variant of . +/// The running runtime's complete catalog of well-known built-in model IDs, including supported models and additional IDs with built-in metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogTrustSnapshotMalformed : CatalogTrustSnapshot +public sealed class BuiltInModelCatalog { - /// - [JsonIgnore] - public override string Status => "malformed"; - - /// Service-computed exposure eligibility. `unknown` is required while Agent Finder returns no explicit eligibility field. - [JsonPropertyName("eligibility")] - public required CatalogTrustEligibility Eligibility { get; set; } - - /// Bounded source and observation time for this snapshot. This is distinct from evidence used by the authority to calculate trust. - [JsonPropertyName("provenance")] - public required CatalogTrustProvenance Provenance { get; set; } - - /// Schema version of this runtime-owned snapshot envelope. - [JsonPropertyName("schemaVersion")] - public required CatalogTrustSnapshotSchemaVersion SchemaVersion { get; set; } + /// Built-in model entries. + [JsonPropertyName("models")] + public IList Models { get => field ??= []; set; } } -/// Catalogue identity retained from a bound candidate or plan at installation time. +/// Built-in tool metadata with identifier, optional namespaced name, description, input-parameter schema, and usage instructions. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class InstallationCatalogueIdentity +public sealed class Tool { - /// Catalogue description retained at install planning time. + /// Description of what the tool does. [JsonPropertyName("description")] - public string? Description { get; set; } - - /// Human display name retained from the catalogue candidate. - [JsonPropertyName("displayName")] - public string DisplayName { get; set; } = string.Empty; - - /// Catalogue item URL when supplied by the authority. - [JsonPropertyName("itemUrl")] - public string? ItemUrl { get; set; } + public string Description { get; set; } = string.Empty; - /// Catalogue publisher retained at install planning time. - [JsonPropertyName("publisher")] - public string? Publisher { get; set; } + /// Optional instructions for how to use this tool effectively. + [JsonPropertyName("instructions")] + public string? Instructions { get; set; } - /// Authority resource identifier when supplied by the catalogue. - [JsonPropertyName("resourceId")] - public string? ResourceId { get; set; } + /// Tool identifier (e.g., "bash", "grep", "str_replace_editor"). + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Catalogue authority/source string that supplied the candidate. - [JsonPropertyName("source")] - public string Source { get; set; } = string.Empty; + /// Optional namespaced name for declarative filtering (e.g., "playwright/navigate" for MCP tools). + [JsonPropertyName("namespacedName")] + public string? NamespacedName { get; set; } - /// Catalogue trust observation retained at install planning time. - [JsonPropertyName("trustAtInstall")] - public CatalogTrustSnapshot? TrustAtInstall { get; set; } + /// JSON Schema for the tool's input parameters. + [JsonPropertyName("parameters")] + public IDictionary? Parameters { get; set; } - /// Catalogue version retained at install planning time. - [JsonPropertyName("version")] - public string? Version { get; set; } + /// Telemetry-safety policy for the tool name and input names, not input values. Treat omitted metadata as unsafe. + [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] + [JsonPropertyName("safeForTelemetry")] + public JsonElement? SafeForTelemetry { get; set; } } -/// Durable configuration ownership is distinct from session-specific usability. +/// Built-in tools available for the requested model, with their parameters and instructions. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpInstallationSummary +public sealed class ToolList { - /// Catalogue identity retained from the installed plan when available. - [JsonPropertyName("catalogue")] - public InstallationCatalogueIdentity? Catalogue { get; set; } - - /// Exact alternative retained in the installing receipt. - [JsonPropertyName("choiceId")] - public string ChoiceId { get; set; } = string.Empty; - - /// Identity retained from the validated original plan. - [JsonPropertyName("identity")] - public McpPlanResourceIdentity Identity { get => field ??= new(); set; } - - /// Exact durable installation receipt identity. - [JsonPropertyName("installationId")] - public string InstallationId { get; set; } = string.Empty; - - /// ISO 8601 wall-clock installation time when available. - [JsonPropertyName("installedAt")] - public string? InstalledAt { get; set; } - - /// Original installing operation, not a fresh management operation. - [JsonPropertyName("operationId")] - public string OperationId { get; set; } = string.Empty; - - /// Ownership or setup state, never inferred proof of tool usability. - [JsonPropertyName("state")] - public McpInstallationState State { get; set; } + /// List of available built-in tools with metadata. + [JsonPropertyName("tools")] + public IList Tools { get => field ??= []; set; } } -/// The listed variant of . +/// Optional model identifier whose tool overrides should be applied to the listing. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationManagementOutcomeListed : McpInstallationManagementOutcome +internal sealed class ToolsListRequest { - /// - [JsonIgnore] - public override string Kind => "listed"; - - /// Owned receipts visible to the selected account and host. - [JsonPropertyName("installations")] - public required IList Installations { get; set; } + /// Optional model ID — when provided, the returned tool list reflects model-specific overrides. + [JsonPropertyName("model")] + public string? Model { get; set; } } -/// The recovered variant of . +/// Quota usage snapshot for a Copilot quota type, including entitlement, used requests, overage, reset date, and remaining percentage. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationManagementOutcomeRecovered : McpInstallationManagementOutcome +public sealed class AccountQuotaSnapshot { - /// - [JsonIgnore] - public override string Kind => "recovered"; + /// Number of requests included in the entitlement, or -1 for unlimited entitlements. + [JsonPropertyName("entitlementRequests")] + public long EntitlementRequests { get; set; } - /// Freshly inspected receipts after successful durable reconciliation. - [JsonPropertyName("installations")] - public required IList Installations { get; set; } -} + /// Whether the user has an unlimited usage entitlement. + [JsonPropertyName("isUnlimitedEntitlement")] + public bool IsUnlimitedEntitlement { get; set; } -/// Exact inert removal plan. No configuration or credentials have changed. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpUninstallPlan -{ - /// Original wall-clock expiry in milliseconds. Applying never renews it. - [JsonPropertyName("expiresAtEpochMs")] - public long ExpiresAtEpochMs { get; set; } + /// Number of additional usage requests made this period. + [JsonPropertyName("overage")] + public double Overage { get; set; } - /// Original owned receipt being removed. - [JsonPropertyName("installation")] - public McpInstallationSummary Installation { get => field ??= new(); set; } + /// Whether additional usage is allowed when quota is exhausted. + [JsonPropertyName("overageAllowedWithExhaustedQuota")] + public bool OverageAllowedWithExhaustedQuota { get; set; } - /// The original operation, inspectable and cancellable on this same connection. - [JsonPropertyName("operationId")] - public string OperationId { get; set; } = string.Empty; + /// Percentage of entitlement remaining. + [JsonPropertyName("remainingPercentage")] + public double RemainingPercentage { get; set; } - /// Exact configured input slots owned by this installation, never shared OAuth tokens. - [JsonPropertyName("ownedSecretCount")] - public long OwnedSecretCount { get; set; } + /// Date when the quota resets (ISO 8601 string). + [JsonPropertyName("resetDate")] + public DateTimeOffset? ResetDate { get; set; } - /// One-use original connection and authority-bound plan handle. - [JsonPropertyName("planHandle")] - public string PlanHandle { get; set; } = string.Empty; + /// Whether usage is still permitted after quota exhaustion. + [JsonPropertyName("usageAllowedWithExhaustedQuota")] + public bool UsageAllowedWithExhaustedQuota { get; set; } - /// Shared authentication is deliberately retained; revocation is a separate action. - [JsonPropertyName("preservesSharedAuthentication")] - public bool PreservesSharedAuthentication { get; set; } + /// Number of requests used so far this period. + [JsonPropertyName("usedRequests")] + public long UsedRequests { get; set; } +} - /// Whether removal restores a protected earlier configuration. - [JsonPropertyName("restoresPreviousConfiguration")] - public bool RestoresPreviousConfiguration { get; set; } +/// Quota usage snapshots for the resolved user, keyed by quota type. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class AccountGetQuotaResult +{ + /// Quota snapshots keyed by type (e.g., chat, completions, premium_interactions). + [JsonPropertyName("quotaSnapshots")] + public IDictionary QuotaSnapshots { get => field ??= new Dictionary(); set; } } -/// The uninstall-planned variant of . +/// RPC data type for AccountGetQuota operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationManagementOutcomeUninstallPlanned : McpInstallationManagementOutcome +internal sealed class AccountGetQuotaRequest { - /// - [JsonIgnore] - public override string Kind => "uninstall-planned"; + /// GitHub token accepted for compatibility with existing SDK clients. When provided, resolves this token instead of using the current account. + [JsonPropertyName("gitHubToken")] + public string? GitHubToken { get; set; } - /// Original owned removal plan and operation. - [JsonPropertyName("plan")] - public required McpUninstallPlan Plan { get; set; } + /// Opaque account identifier returned by `account.getAllUsers`. When omitted, the current account is used. + [JsonPropertyName("selectionId")] + public string? SelectionId { get; set; } } -/// Status snapshot from the original connection, independent of new-work account availability. -/// Polymorphic base type discriminated by phase. +/// Authentication credentials accepted only at native protocol ingress. Runtime outputs use credential-free `AuthIdentity` metadata. +/// Polymorphic base type discriminated by type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonPolymorphic( - TypeDiscriminatorPropertyName = "phase", + TypeDiscriminatorPropertyName = "type", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(McpInstallationOperationStatusPreparing), "preparing")] -[JsonDerivedType(typeof(McpInstallationOperationStatusPrepared), "prepared")] -[JsonDerivedType(typeof(McpInstallationOperationStatusAwaitingConfirmation), "awaiting-confirmation")] -[JsonDerivedType(typeof(McpInstallationOperationStatusRevalidating), "revalidating")] -[JsonDerivedType(typeof(McpInstallationOperationStatusApplying), "applying")] -[JsonDerivedType(typeof(McpInstallationOperationStatusCompleted), "completed")] -public partial class McpInstallationOperationStatus +[JsonDerivedType(typeof(AuthInfoHmac), "hmac")] +[JsonDerivedType(typeof(AuthInfoEnv), "env")] +[JsonDerivedType(typeof(AuthInfoToken), "token")] +[JsonDerivedType(typeof(AuthInfoTokenProvider), "token-provider")] +[JsonDerivedType(typeof(AuthInfoCopilotApiToken), "copilot-api-token")] +[JsonDerivedType(typeof(AuthInfoUser), "user")] +[JsonDerivedType(typeof(AuthInfoAccount), "account")] +[JsonDerivedType(typeof(AuthInfoGhCli), "gh-cli")] +[JsonDerivedType(typeof(AuthInfoApiKey), "api-key")] +public partial class AuthInfo { /// The type discriminator. - [JsonPropertyName("phase")] - public virtual string Phase { get; set; } = string.Empty; + [JsonPropertyName("type")] + public virtual string Type { get; set; } = string.Empty; } -/// The preparing variant of . +/// Endpoint URLs from the raw Copilot `/copilot_internal/v2/token` user-response passthrough. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOperationStatusPreparing : McpInstallationOperationStatus +public sealed class CopilotUserResponseEndpoints { - /// - [JsonIgnore] - public override string Phase => "preparing"; + /// Copilot API endpoint URL. + [JsonPropertyName("api")] + public string? Api { get; set; } - /// Whether cancellation has been requested, not proof that a write was undone. - [JsonPropertyName("cancellationRequested")] - public required bool CancellationRequested { get; set; } + /// Experimental-service endpoint URL. + [JsonPropertyName("exp")] + public string? Exp { get; set; } - /// Original runtime-issued operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } -} + /// Origin-tracker endpoint URL. + [JsonPropertyName("origin-tracker")] + public string? OriginTracker { get; set; } -/// The prepared variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOperationStatusPrepared : McpInstallationOperationStatus -{ - /// - [JsonIgnore] - public override string Phase => "prepared"; + /// Copilot proxy endpoint URL. + [JsonPropertyName("proxy")] + public string? Proxy { get; set; } - /// Whether the inert prepared operation was asked to cancel. - [JsonPropertyName("cancellationRequested")] - public required bool CancellationRequested { get; set; } + /// Copilot telemetry endpoint URL. + [JsonPropertyName("telemetry")] + public string? Telemetry { get; set; } +} - /// Original runtime-issued operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } +/// RPC data type for CopilotUserResponseEnterpriseListItem operations. +public sealed class CopilotUserResponseEnterpriseListItem +{ + /// JavaScript-safe numeric database ID of the enterprise. + [JsonPropertyName("id")] + public long? Id { get; set; } } -/// The awaiting-confirmation variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOperationStatusAwaitingConfirmation : McpInstallationOperationStatus +/// RPC data type for CopilotUserResponseOrganizationListItem operations. +public sealed class CopilotUserResponseOrganizationListItem { - /// - [JsonIgnore] - public override string Phase => "awaiting-confirmation"; + /// Numeric database ID of the organization. + [JsonPropertyName("id")] + public double? Id { get; set; } - /// Whether the pending human callback was asked to cancel. - [JsonPropertyName("cancellationRequested")] - public required bool CancellationRequested { get; set; } + /// GitHub login of the organization. + [JsonPropertyName("login")] + public string? Login { get; set; } - /// Original runtime-issued operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// Display name of the organization. + [JsonPropertyName("name")] + public string? Name { get; set; } } -/// The revalidating variant of . +/// Chat quota snapshot from the raw Copilot user-response passthrough, with entitlement, overage, remaining quota, reset, and billing fields. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOperationStatusRevalidating : McpInstallationOperationStatus +public sealed class CopilotUserResponseQuotaSnapshotsChat { - /// - [JsonIgnore] - public override string Phase => "revalidating"; + /// Number of requests/units included in the entitlement for this period; `-1` denotes an unlimited entitlement. + [JsonPropertyName("entitlement")] + public double? Entitlement { get; set; } - /// Whether source or authority revalidation was asked to cancel. - [JsonPropertyName("cancellationRequested")] - public required bool CancellationRequested { get; set; } + /// Whether the user currently has quota available; when `false` and not unlimited, further requests are blocked until the quota resets. + [JsonPropertyName("has_quota")] + public bool? HasQuota { get; set; } - /// Original runtime-issued operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } -} + /// Count of additional pay-per-request usage consumed this period beyond the entitlement. + [JsonPropertyName("overage_count")] + public double? OverageCount { get; set; } -/// The applying variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOperationStatusApplying : McpInstallationOperationStatus -{ - /// - [JsonIgnore] - public override string Phase => "applying"; + /// Whether usage may continue at pay-per-request rates once the entitlement is exhausted. + [JsonPropertyName("overage_permitted")] + public bool? OveragePermitted { get; set; } - /// Whether applying was asked to cancel; already-started effects retain their lease. - [JsonPropertyName("cancellationRequested")] - public required bool CancellationRequested { get; set; } + /// Percentage of the entitlement remaining at the snapshot timestamp. + [JsonPropertyName("percent_remaining")] + public double? PercentRemaining { get; set; } - /// Original runtime-issued operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } -} + /// Identifier of the quota bucket this snapshot describes. + [JsonPropertyName("quota_id")] + public string? QuotaId { get; set; } -/// Terminal mutation result. Uncertainty is not approval, rollback or permission to replay. -/// Polymorphic base type discriminated by kind. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(McpInstallationOutcomeInstalled), "installed")] -[JsonDerivedType(typeof(McpInstallationOutcomeUninstalled), "uninstalled")] -[JsonDerivedType(typeof(McpInstallationOutcomeRolledBack), "rolled-back")] -[JsonDerivedType(typeof(McpInstallationOutcomeRecoveryRequired), "recovery-required")] -[JsonDerivedType(typeof(McpInstallationOutcomeDeclined), "declined")] -[JsonDerivedType(typeof(McpInstallationOutcomeCancelled), "cancelled")] -[JsonDerivedType(typeof(McpInstallationOutcomeRefused), "refused")] -public partial class McpInstallationOutcome -{ - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// Amount of quota remaining at the snapshot timestamp. + [JsonPropertyName("quota_remaining")] + public double? QuotaRemaining { get; set; } + /// Unix epoch time, in seconds, when this quota next resets. + [JsonPropertyName("quota_reset_at")] + public double? QuotaResetAt { get; set; } -/// The installed variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOutcomeInstalled : McpInstallationOutcome -{ - /// - [JsonIgnore] - public override string Kind => "installed"; + /// Remaining entitlement/quota amount at the snapshot timestamp. + [JsonPropertyName("remaining")] + public double? Remaining { get; set; } - /// Durable installation succeeded but final transaction cleanup remains. - [JsonPropertyName("cleanupPending")] - public required bool CleanupPending { get; set; } + /// UTC timestamp when this snapshot was captured. + [JsonPropertyName("timestamp_utc")] + public string? TimestampUtc { get; set; } - /// Receipt identity produced by the confirmed transaction. - [JsonPropertyName("installation")] - public required McpInstallationSummary Installation { get; set; } + /// Whether this category uses usage-based (token/AI-credit) billing rather than a fixed premium-request count. + [JsonPropertyName("token_based_billing")] + public bool? TokenBasedBilling { get; set; } + + /// Whether the entitlement for this category is unlimited. + [JsonPropertyName("unlimited")] + public bool? Unlimited { get; set; } } -/// The uninstalled variant of . +/// Completions quota snapshot from the raw Copilot user-response passthrough, with entitlement, overage, remaining quota, reset, and billing fields. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOutcomeUninstalled : McpInstallationOutcome +public sealed class CopilotUserResponseQuotaSnapshotsCompletions { - /// - [JsonIgnore] - public override string Kind => "uninstalled"; + /// Number of requests/units included in the entitlement for this period; `-1` denotes an unlimited entitlement. + [JsonPropertyName("entitlement")] + public double? Entitlement { get; set; } - /// Durable removal succeeded but final cleanup remains. - [JsonPropertyName("cleanupPending")] - public required bool CleanupPending { get; set; } + /// Whether the user currently has quota available; when `false` and not unlimited, further requests are blocked until the quota resets. + [JsonPropertyName("has_quota")] + public bool? HasQuota { get; set; } - /// Exact removed receipt identity. - [JsonPropertyName("installationId")] - public required string InstallationId { get; set; } + /// Count of additional pay-per-request usage consumed this period beyond the entitlement. + [JsonPropertyName("overage_count")] + public double? OverageCount { get; set; } - /// Original removal operation. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// Whether usage may continue at pay-per-request rates once the entitlement is exhausted. + [JsonPropertyName("overage_permitted")] + public bool? OveragePermitted { get; set; } - /// Any grants in the incumbent shared OAuth store remain unowned and retained. - [JsonPropertyName("preservedSharedAuthentication")] - public required bool PreservedSharedAuthentication { get; set; } + /// Percentage of the entitlement remaining at the snapshot timestamp. + [JsonPropertyName("percent_remaining")] + public double? PercentRemaining { get; set; } - /// Exact owned input slots removed, excluding shared OAuth credentials. - [JsonPropertyName("removedOwnedSecrets")] - public required long RemovedOwnedSecrets { get; set; } + /// Identifier of the quota bucket this snapshot describes. + [JsonPropertyName("quota_id")] + public string? QuotaId { get; set; } - /// Whether protected pre-install configuration was restored. - [JsonPropertyName("restoredPreviousConfiguration")] - public required bool RestoredPreviousConfiguration { get; set; } + /// Amount of quota remaining at the snapshot timestamp. + [JsonPropertyName("quota_remaining")] + public double? QuotaRemaining { get; set; } + + /// Unix epoch time, in seconds, when this quota next resets. + [JsonPropertyName("quota_reset_at")] + public double? QuotaResetAt { get; set; } + + /// Remaining entitlement/quota amount at the snapshot timestamp. + [JsonPropertyName("remaining")] + public double? Remaining { get; set; } + + /// UTC timestamp when this snapshot was captured. + [JsonPropertyName("timestamp_utc")] + public string? TimestampUtc { get; set; } + + /// Whether this category uses usage-based (token/AI-credit) billing rather than a fixed premium-request count. + [JsonPropertyName("token_based_billing")] + public bool? TokenBasedBilling { get; set; } + + /// Whether the entitlement for this category is unlimited. + [JsonPropertyName("unlimited")] + public bool? Unlimited { get; set; } } -/// The durable transaction was aborted or fully compensated. -/// The rolled-back variant of . +/// Premium-interactions quota snapshot from the raw Copilot user-response passthrough, with entitlement, overage, remaining quota, reset, and billing fields. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOutcomeRolledBack : McpInstallationOutcome +public sealed class CopilotUserResponseQuotaSnapshotsPremiumInteractions { - /// - [JsonIgnore] - public override string Kind => "rolled-back"; + /// Number of requests/units included in the entitlement for this period; `-1` denotes an unlimited entitlement. + [JsonPropertyName("entitlement")] + public double? Entitlement { get; set; } - /// Original connection-owned operation. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// Whether the user currently has quota available; when `false` and not unlimited, further requests are blocked until the quota resets. + [JsonPropertyName("has_quota")] + public bool? HasQuota { get; set; } - /// Cause of the fully aborted or compensated operation. - [JsonPropertyName("reason")] - public required McpInstallationFailureReason Reason { get; set; } + /// Count of additional pay-per-request usage consumed this period beyond the entitlement. + [JsonPropertyName("overage_count")] + public double? OverageCount { get; set; } + + /// Whether usage may continue at pay-per-request rates once the entitlement is exhausted. + [JsonPropertyName("overage_permitted")] + public bool? OveragePermitted { get; set; } + + /// Percentage of the entitlement remaining at the snapshot timestamp. + [JsonPropertyName("percent_remaining")] + public double? PercentRemaining { get; set; } + + /// Identifier of the quota bucket this snapshot describes. + [JsonPropertyName("quota_id")] + public string? QuotaId { get; set; } + + /// Amount of quota remaining at the snapshot timestamp. + [JsonPropertyName("quota_remaining")] + public double? QuotaRemaining { get; set; } + + /// Unix epoch time, in seconds, when this quota next resets. + [JsonPropertyName("quota_reset_at")] + public double? QuotaResetAt { get; set; } + + /// Remaining entitlement/quota amount at the snapshot timestamp. + [JsonPropertyName("remaining")] + public double? Remaining { get; set; } + + /// UTC timestamp when this snapshot was captured. + [JsonPropertyName("timestamp_utc")] + public string? TimestampUtc { get; set; } + + /// Whether this category uses usage-based (token/AI-credit) billing rather than a fixed premium-request count. + [JsonPropertyName("token_based_billing")] + public bool? TokenBasedBilling { get; set; } + + /// Whether the entitlement for this category is unlimited. + [JsonPropertyName("unlimited")] + public bool? Unlimited { get; set; } } -/// A write may have completed. Recover and inspect durable state before retrying. -/// The recovery-required variant of . +/// Quota snapshot map from the raw Copilot user-response passthrough, with chat, completions, premium-interactions, and other entries. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOutcomeRecoveryRequired : McpInstallationOutcome +public sealed class CopilotUserResponseQuotaSnapshots { - /// - [JsonIgnore] - public override string Kind => "recovery-required"; + /// Chat quota snapshot from the raw Copilot user-response passthrough, with entitlement, overage, remaining quota, reset, and billing fields. + [JsonPropertyName("chat")] + public CopilotUserResponseQuotaSnapshotsChat? Chat { get; set; } - /// Operation whose durable result must be recovered and inspected. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// Completions quota snapshot from the raw Copilot user-response passthrough, with entitlement, overage, remaining quota, reset, and billing fields. + [JsonPropertyName("completions")] + public CopilotUserResponseQuotaSnapshotsCompletions? Completions { get; set; } + + /// Premium-interactions quota snapshot from the raw Copilot user-response passthrough, with entitlement, overage, remaining quota, reset, and billing fields. + [JsonPropertyName("premium_interactions")] + public CopilotUserResponseQuotaSnapshotsPremiumInteractions? PremiumInteractions { get; set; } } -/// The declined variant of . +/// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOutcomeDeclined : McpInstallationOutcome +public sealed class CopilotUserResponse { - /// - [JsonIgnore] - public override string Kind => "declined"; + /// Copilot access SKU identifier (e.g. `free_limited_copilot`, `copilot_for_business_seat_quota`) used to gate model and feature access. + [JsonPropertyName("access_type_sku")] + public string? AccessTypeSku { get; set; } - /// Original operation explicitly declined by the user. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// Opaque analytics tracking identifier for the user, forwarded from the Copilot API. + [JsonPropertyName("analytics_tracking_id")] + public string? AnalyticsTrackingId { get; set; } + + /// Date the Copilot seat was assigned to the user, if applicable. + [JsonPropertyName("assigned_date")] + public string? AssignedDate { get; set; } + + /// Whether the user is eligible to sign up for the free/limited Copilot tier. + [JsonPropertyName("can_signup_for_limited")] + public bool? CanSignupForLimited { get; set; } + + /// Whether the user is able to upgrade their Copilot plan. + [JsonPropertyName("can_upgrade_plan")] + public bool? CanUpgradePlan { get; set; } + + /// Whether Copilot chat is enabled for the user. + [JsonPropertyName("chat_enabled")] + public bool? ChatEnabled { get; set; } + + /// Whether CLI remote control is enabled for the user. + [JsonPropertyName("cli_remote_control_enabled")] + public bool? CliRemoteControlEnabled { get; set; } + + /// Whether cloud session storage is enabled for the user. + [JsonPropertyName("cloud_session_storage_enabled")] + public bool? CloudSessionStorageEnabled { get; set; } + + /// Whether the Codex agent is enabled for the user. + [JsonPropertyName("codex_agent_enabled")] + public bool? CodexAgentEnabled { get; set; } + + /// Copilot plan name for the user (e.g. `individual`, `business`, `enterprise`). + [JsonPropertyName("copilot_plan")] + public string? CopilotPlan { get; set; } + + /// Whether `.copilotignore` content-exclusion support is enabled for the user. + [JsonPropertyName("copilotignore_enabled")] + public bool? CopilotignoreEnabled { get; set; } + + /// Endpoint URLs from the raw Copilot `/copilot_internal/v2/token` user-response passthrough. + [JsonPropertyName("endpoints")] + public CopilotUserResponseEndpoints? Endpoints { get; set; } + + /// Enterprises that provide the user's Copilot license; malformed entries are normalized to null or ID-less shapes. + [JsonPropertyName("enterprise_list")] + public IList? EnterpriseList { get; set; } + + /// Whether MCP (Model Context Protocol) support is enabled for the user. + [JsonPropertyName("is_mcp_enabled")] + public bool? IsMcpEnabled { get; set; } + + /// Whether the user is a GitHub/Microsoft staff member. + [JsonPropertyName("is_staff")] + public bool? IsStaff { get; set; } + + /// Per-category quota allotments for free/limited-tier users, keyed by quota category. + [JsonPropertyName("limited_user_quotas")] + public IDictionary? LimitedUserQuotas { get; set; } + + /// Date the free/limited-tier user's quotas next reset, as a raw string from the Copilot API. + [JsonPropertyName("limited_user_reset_date")] + public string? LimitedUserResetDate { get; set; } + + /// GitHub login of the authenticated user. + [JsonPropertyName("login")] + public string? Login { get; set; } + + /// Per-category monthly quota allotments, keyed by quota category. + [JsonPropertyName("monthly_quotas")] + public IDictionary? MonthlyQuotas { get; set; } + + /// Organizations the user belongs to, each with an optional ID, login, and display name. + [JsonPropertyName("organization_list")] + public IList? OrganizationList { get; set; } + + /// Logins of the organizations the user belongs to. + [JsonPropertyName("organization_login_list")] + public IList? OrganizationLoginList { get; set; } + + /// Date the user's usage quota next resets, as a raw string from the Copilot API; see `quota_reset_date_utc` for the UTC-normalized value. + [JsonPropertyName("quota_reset_date")] + public string? QuotaResetDate { get; set; } + + /// UTC-normalized form of `quota_reset_date` (the date the user's usage quota next resets). + [JsonPropertyName("quota_reset_date_utc")] + public string? QuotaResetDateUtc { get; set; } + + /// Quota snapshot map from the raw Copilot user-response passthrough, with chat, completions, premium-interactions, and other entries. + [JsonPropertyName("quota_snapshots")] + public CopilotUserResponseQuotaSnapshots? QuotaSnapshots { get; set; } + + /// Whether the user's telemetry is subject to restricted-data handling. + [JsonPropertyName("restricted_telemetry")] + public bool? RestrictedTelemetry { get; set; } + + /// Raw passthrough of the Copilot API `te` flag for the user (an opaque server-side eligibility signal surfaced in telemetry); not otherwise interpreted by the runtime. + [JsonPropertyName("te")] + public bool? Te { get; set; } + + /// Whether the account is on usage-based (token/AI-credit) billing rather than a fixed premium-request quota. + [JsonPropertyName("token_based_billing")] + public bool? TokenBasedBilling { get; set; } } -/// The cancelled variant of . +/// Authentication-info input variant for GitHub-internal HMAC auth, carrying the public GitHub host and HMAC secret. +/// The hmac variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOutcomeCancelled : McpInstallationOutcome +public partial class AuthInfoHmac : AuthInfo { /// [JsonIgnore] - public override string Kind => "cancelled"; + public override string Type => "hmac"; - /// Original operation cancelled before a terminal application result. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("copilotUser")] + public CopilotUserResponse? CopilotUser { get; set; } + + /// HMAC secret used to sign requests. + [JsonPropertyName("hmac")] + public required string Hmac { get; set; } + + /// Authentication host. HMAC auth always targets the public GitHub host. + [JsonPropertyName("host")] + public required string Host { get; set; } } -/// The refused variant of . +/// Authentication-info input variant for a token sourced from an environment variable, with host, optional login, token, and env var name. +/// The env variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOutcomeRefused : McpInstallationOutcome +public partial class AuthInfoEnv : AuthInfo { /// [JsonIgnore] - public override string Kind => "refused"; + public override string Type => "env"; - /// Present once an operation has been allocated; never a plan handle. + /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("operationId")] - public string? OperationId { get; set; } + [JsonPropertyName("copilotUser")] + public CopilotUserResponse? CopilotUser { get; set; } - /// Specific bounded refusal, never a success-shaped fallback. - [JsonPropertyName("reason")] - public required McpInstallationFailureReason Reason { get; set; } + /// Name of the environment variable the token was sourced from. + [JsonPropertyName("envVar")] + public required string EnvVar { get; set; } + + /// Authentication host (e.g. https://github.com or a GHES host). + [JsonPropertyName("host")] + public required string Host { get; set; } + + /// User login associated with the token. Undefined for server-to-server tokens (those starting with `ghs_`). + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("login")] + public string? Login { get; set; } + + /// The token value itself. Treat as a secret. + [JsonPropertyName("token")] + public required string Token { get; set; } } -/// The completed variant of . +/// Authentication-info input variant for SDK-configured token authentication, carrying host and the secret token value. +/// The token variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOperationStatusCompleted : McpInstallationOperationStatus +public partial class AuthInfoToken : AuthInfo { /// [JsonIgnore] - public override string Phase => "completed"; + public override string Type => "token"; - /// Whether cancellation was requested before the terminal result. - [JsonPropertyName("cancellationRequested")] - public required bool CancellationRequested { get; set; } + /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("copilotUser")] + public CopilotUserResponse? CopilotUser { get; set; } - /// Original runtime-issued operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// Authentication host. + [JsonPropertyName("host")] + public required string Host { get; set; } - /// Immutable terminal receipt. - [JsonPropertyName("outcome")] - public required McpInstallationOutcome Outcome { get; set; } + /// Opaque native GitHub credential registration backing this token identity, when applicable. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("registrationId")] + public string? RegistrationId { get; set; } + + /// The token value itself. Treat as a secret. + [JsonPropertyName("token")] + public required string Token { get; set; } } -/// The operation variant of . +/// Authentication-info variant backed by an SDK GitHub token callback. It carries routing metadata but never a plaintext token. +/// The token-provider variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationManagementOutcomeOperation : McpInstallationManagementOutcome +public partial class AuthInfoTokenProvider : AuthInfo { /// [JsonIgnore] - public override string Kind => "operation"; + public override string Type => "token-provider"; - /// Original-connection operation snapshot. - [JsonPropertyName("operation")] - public required McpInstallationOperationStatus Operation { get; set; } + /// Snapshot of the authenticated user's Copilot subscription info, if known. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("copilotUser")] + public CopilotUserResponse? CopilotUser { get; set; } + + /// Authentication host. + [JsonPropertyName("host")] + public required string Host { get; set; } + + /// Opaque SDK callback registration identifier. + [JsonPropertyName("registrationId")] + public required string RegistrationId { get; set; } } -/// The refused variant of . +/// Authentication-info variant for direct Copilot API token auth sourced from environment variables, with public GitHub host. +/// The copilot-api-token variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationManagementOutcomeRefused : McpInstallationManagementOutcome +public partial class AuthInfoCopilotApiToken : AuthInfo { /// [JsonIgnore] - public override string Kind => "refused"; + public override string Type => "copilot-api-token"; - /// Specific bounded refusal. - [JsonPropertyName("reason")] - public required McpInstallationFailureReason Reason { get; set; } + /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("copilotUser")] + public CopilotUserResponse? CopilotUser { get; set; } + + /// Authentication host (always the public GitHub host). + [JsonPropertyName("host")] + public required string Host { get; set; } } -/// The outcome variant of . +/// Authentication-info variant for OAuth user auth, with host and login; the token remains in the runtime secret store. +/// The user variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationManagementResultOutcome : McpInstallationManagementResult +public partial class AuthInfoUser : AuthInfo { /// [JsonIgnore] - public override string Kind => "outcome"; + public override string Type => "user"; - /// Capabilities actually honoured for this request. - [JsonPropertyName("negotiated")] - public required CatalogNegotiatedContract Negotiated { get; set; } + /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("copilotUser")] + public CopilotUserResponse? CopilotUser { get; set; } - /// Observed management outcome. - [JsonPropertyName("outcome")] - public required McpInstallationManagementOutcome Outcome { get; set; } + /// Authentication host. + [JsonPropertyName("host")] + public required string Host { get; set; } + + /// OAuth user login. + [JsonPropertyName("login")] + public required string Login { get; set; } } -/// The caller's protocol version or required capabilities cannot be honoured. Returned instead of a partial or ambiguous success. -/// The negotiation-refused variant of . +/// An interactive account whose model provider owns its credentials. It carries no GitHub credential. +/// The account variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationManagementResultNegotiationRefused : McpInstallationManagementResult +public partial class AuthInfoAccount : AuthInfo { /// [JsonIgnore] - public override string Kind => "negotiation-refused"; + public override string Type => "account"; - /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } - - /// Lowest caller protocol version this runtime will serve. - [JsonPropertyName("minimumSupportedProtocolVersion")] - public required long MinimumSupportedProtocolVersion { get; set; } - - /// Whether the version or the capability set was the problem. - [JsonPropertyName("reason")] - public required CatalogNegotiationRefusedReason Reason { get; set; } - - /// Protocol version of the runtime that refused the request. - [JsonPropertyName("runtimeProtocolVersion")] - public required long RuntimeProtocolVersion { get; set; } - - /// Capabilities this runtime can safely advertise to this caller. The complete five-capability protocol-3 legacy set is always present; every capability added after that baseline appears only when the caller required it, so an older closed-enum decoder can still consume a refusal. This list does not imply that every deployment has enabled every operation. - [JsonPropertyName("supportedCapabilities")] - public required IList SupportedCapabilities { get; set; } + /// Host coordinate owned by the account's model provider. + [JsonPropertyName("host")] + public required string Host { get; set; } - /// The subset of the caller's bounded extensible capability identifiers this runtime cannot honour. - [JsonPropertyName("unsupportedCapabilities")] - public required IList UnsupportedCapabilities { get; set; } + /// Login identifying the provider-owned account. + [JsonPropertyName("login")] + public required string Login { get; set; } } -/// The request was rejected because a bounded field fell outside its permitted range or a required field was unusable. Pagination may also be rejected by the authority after a continuation request; repeat the search without page. -/// The invalid-request variant of . +/// Authentication-info input variant for GitHub CLI credentials, carrying host, login, and the `gh auth token` value. +/// The gh-cli variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationManagementResultInvalidRequest : McpInstallationManagementResult +public partial class AuthInfoGhCli : AuthInfo { /// [JsonIgnore] - public override string Kind => "invalid-request"; + public override string Type => "gh-cli"; - /// Which request field was rejected. - [JsonPropertyName("field")] - public required CatalogInvalidRequestField Field { get; set; } + /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("copilotUser")] + public CopilotUserResponse? CopilotUser { get; set; } - /// Human-readable explanation, safe to surface. Never echoes the offending value, nor a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Authentication host. + [JsonPropertyName("host")] + public required string Host { get; set; } + + /// User login as reported by `gh auth status`. + [JsonPropertyName("login")] + public required string Login { get; set; } + + /// The token returned by `gh auth token`. Treat as a secret. + [JsonPropertyName("token")] + public required string Token { get; set; } } -/// One Registry string-valued configuration entry for the selected transport. +/// Authentication-info input variant for API-key authentication to a non-GitHub LLM provider, carrying the secret `apiKey` and host. +/// The api-key variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpInstallationInput +public partial class AuthInfoApiKey : AuthInfo { - /// Exact category declared by the selected choice. - [JsonPropertyName("category")] - public McpPlanValueCategory Category { get; set; } + /// + [JsonIgnore] + public override string Type => "api-key"; - /// Exact key declared by the selected choice. - [JsonPropertyName("key")] - public string Key { get; set; } = string.Empty; + /// The API key. Treat as a secret. + [JsonPropertyName("apiKey")] + public required string ApiKey { get; set; } - /// Explicit non-secret value. Secret placeholders use a separate input channel. - [JsonPropertyName("value")] - public string Value { get; set; } = string.Empty; + /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("copilotUser")] + public CopilotUserResponse? CopilotUser { get; set; } + + /// Authentication host. + [JsonPropertyName("host")] + public required string Host { get; set; } } -/// A request-local value for one exact reviewed placeholder. Never logged or persisted in a plan. +/// Current authentication state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpInstallationSecret +public sealed class AccountGetCurrentAuthResult { - /// Exact placeholder from the selected choice, not a caller-chosen backend identifier. - [JsonPropertyName("placeholder")] - public string Placeholder { get; set; } = string.Empty; + /// Authentication errors from the last auth attempt, if any. + [JsonPropertyName("authErrors")] + public IList? AuthErrors { get; set; } - /// Fresh explicit secret value. It is omitted from confirmation reviews and telemetry. - [JsonPropertyName("value")] - public string Value { get; set; } = string.Empty; + /// Current authentication information, if authenticated. + [JsonPropertyName("authInfo")] + public AuthInfo? AuthInfo { get; set; } } -/// Side-effect-free preparation of one original bound remote MCP choice. +/// Authenticated account entry returned by `account.getAllUsers`. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpPrepareInstallRequest +public sealed class AccountAllUsers { - /// Exact selected alternative from that plan. - [JsonPropertyName("choiceId")] - public string ChoiceId { get; set; } = string.Empty; - - /// Required bound catalogue and confirmed remote installation capabilities. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); set; } + /// Authentication information for this user. + [JsonPropertyName("authInfo")] + public AuthInfo AuthInfo { get => field ??= new(); set; } - /// - /// Declared non-secret values. Non-empty only when the caller requires - /// `mcp-configured-remote-installation`; omitted values use the card default. - /// - [JsonPropertyName("inputs")] - public IList Inputs { get => field ??= []; set; } + /// Opaque identifier accepted by account and model selection APIs. + [JsonPropertyName("selectionId")] + public string? SelectionId { get; set; } - /// Original single-use bound plan, never a client-authored configuration. - [JsonPropertyName("planHandle")] - public string PlanHandle { get; set; } = string.Empty; + /// Associated token, if available. + [JsonPropertyName("token")] + public string? Token { get; set; } +} - /// An existing local session attached to this connection, not permission to attach one. - [JsonPropertyName("policySessionId")] - public string PolicySessionId { get; set; } = string.Empty; +/// Result of a successful login; throws on failure. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class AccountLoginResult +{ + /// Whether the credential was persisted to a secure store (system keychain, or the config file when plaintext storage is enabled). False when no secure store was available and the token was not saved, so the consumer can decide how to proceed. + [JsonPropertyName("storedInVault")] + public bool StoredInVault { get; set; } +} - /// - /// One entry per declared secret placeholder of the selected choice. Non-empty - /// only when the caller requires `mcp-configured-remote-installation`. - /// - [JsonPropertyName("secrets")] - public IList Secrets { get => field ??= []; set; } +/// Credentials to validate and store. Omit login to resolve the authenticated user from the token. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class AccountLoginRequest +{ + /// GitHub host URL. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; - /// The trusted host presents this choice alongside the exact secret placeholders. - [JsonPropertyName("secretStorage")] - public McpInstallationSecretStorage SecretStorage { get; set; } + /// User login/username. When omitted, the runtime validates the token and resolves the login from GitHub. + [JsonPropertyName("login")] + public string? Login { get; set; } - /// The exact original source, used transiently only after confirmation. - [JsonPropertyName("source")] - public McpServerCardReference Source { get => field ??= new(); set; } + /// GitHub authentication token. + [JsonPropertyName("token")] + public string Token { get; set; } = string.Empty; } -/// An installation result together with the exact honoured contract, or a negotiation refusal. -/// Polymorphic base type discriminated by kind. +/// Logout result indicating if more users remain. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(McpInstallationResultOutcome), "outcome")] -[JsonDerivedType(typeof(McpInstallationResultNegotiationRefused), "negotiation-refused")] -[JsonDerivedType(typeof(McpInstallationResultInvalidRequest), "invalid-request")] -public partial class McpInstallationResult +public sealed class AccountLogoutResult { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; + /// Whether other authenticated users remain after logout. + [JsonPropertyName("hasMoreUsers")] + public bool HasMoreUsers { get; set; } } - -/// The outcome variant of . +/// User to log out. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationResultOutcome : McpInstallationResult +internal sealed class AccountLogoutRequest { - /// - [JsonIgnore] - public override string Kind => "outcome"; - - /// Capabilities actually honoured for this request. - [JsonPropertyName("negotiated")] - public required CatalogNegotiatedContract Negotiated { get; set; } + /// Authentication information for the user to log out. + [JsonPropertyName("authInfo")] + public AuthInfo? AuthInfo { get; set; } - /// Terminal result of the original operation. - [JsonPropertyName("outcome")] - public required McpInstallationOutcome Outcome { get; set; } + /// Opaque account identifier returned by `account.getAllUsers`. + [JsonPropertyName("selectionId")] + public string? SelectionId { get; set; } } -/// The caller's protocol version or required capabilities cannot be honoured. Returned instead of a partial or ambiguous success. -/// The negotiation-refused variant of . +/// The wording the runtime uses for an authentication identity. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationResultNegotiationRefused : McpInstallationResult +internal sealed class AccountDescribeAuthInfoResult { - /// - [JsonIgnore] - public override string Kind => "negotiation-refused"; + /// One-line description of the identity, suitable after a verb such as "Switched to". + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; - /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] + /// Sign-in banner announcing the authenticated user. [JsonPropertyName("message")] - public required string Message { get; set; } + public string Message { get; set; } = string.Empty; +} - /// Lowest caller protocol version this runtime will serve. - [JsonPropertyName("minimumSupportedProtocolVersion")] - public required long MinimumSupportedProtocolVersion { get; set; } +/// Credential-free authentication identity safe to expose to hosts and user interfaces. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class AuthIdentity +{ + /// Snapshot of the authenticated user's Copilot subscription info, if known. + [JsonPropertyName("copilotUser")] + public CopilotUserResponse? CopilotUser { get; set; } - /// Whether the version or the capability set was the problem. - [JsonPropertyName("reason")] - public required CatalogNegotiationRefusedReason Reason { get; set; } + /// Name of the environment variable that supplied the credential, when applicable. + [JsonPropertyName("envVar")] + public string? EnvVar { get; set; } - /// Protocol version of the runtime that refused the request. - [JsonPropertyName("runtimeProtocolVersion")] - public required long RuntimeProtocolVersion { get; set; } + /// Authentication host. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; - /// Capabilities this runtime can safely advertise to this caller. The complete five-capability protocol-3 legacy set is always present; every capability added after that baseline appears only when the caller required it, so an older closed-enum decoder can still consume a refusal. This list does not imply that every deployment has enabled every operation. - [JsonPropertyName("supportedCapabilities")] - public required IList SupportedCapabilities { get; set; } + /// Authenticated login, when available. + [JsonPropertyName("login")] + public string? Login { get; set; } - /// The subset of the caller's bounded extensible capability identifiers this runtime cannot honour. - [JsonPropertyName("unsupportedCapabilities")] - public required IList UnsupportedCapabilities { get; set; } + /// Opaque SDK GitHub credential registration backing this identity. Routing metadata only; never a credential. + [JsonPropertyName("registrationId")] + public string? RegistrationId { get; set; } + + /// Authentication type. + [JsonPropertyName("type")] + public AuthInfoType Type { get; set; } } -/// The request was rejected because a bounded field fell outside its permitted range or a required field was unusable. Pagination may also be rejected by the authority after a continuation request; repeat the search without page. -/// The invalid-request variant of . +/// The authentication identity whose shared wording should be rendered. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationResultInvalidRequest : McpInstallationResult +internal sealed class AccountDescribeAuthInfoRequest { - /// - [JsonIgnore] - public override string Kind => "invalid-request"; - - /// Which request field was rejected. - [JsonPropertyName("field")] - public required CatalogInvalidRequestField Field { get; set; } - - /// Human-readable explanation, safe to surface. Never echoes the offending value, nor a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Identity to describe. + [JsonPropertyName("identity")] + public AuthIdentity Identity { get => field ??= new(); set; } } -/// Applies exactly one previously prepared operation on its original connection. +/// Whether the identity can create gists, and why not when it cannot. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpApplyInstallRequest +internal sealed class AccountGetGistSupportResult { - /// Capabilities required by the original prepared operation. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); set; } + /// Machine-readable reason category when unsupported. + [JsonPropertyName("kind")] + public string? Kind { get; set; } - /// Runtime-issued ID already returned by prepareInstall, never reused or rebound. - [JsonPropertyName("operationId")] - public string OperationId { get; set; } = string.Empty; + /// Human-readable explanation to show where the gist action would have been. + [JsonPropertyName("reason")] + public string? Reason { get; set; } - /// Same existing attached or privately borrowed session as preparation. - [JsonPropertyName("policySessionId")] - public string PolicySessionId { get; set; } = string.Empty; + /// True when the identity can create gists. + [JsonPropertyName("supported")] + public bool Supported { get; set; } } -/// Read-only preparation of one owned removal under fresh selected-session authority. +/// The authentication identity whose gist support should be reported. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpPlanUninstallRequest +internal sealed class AccountGetGistSupportRequest { - /// Required authenticated bound installation capabilities. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); set; } + /// Identity to evaluate. + [JsonPropertyName("identity")] + public AuthIdentity Identity { get => field ??= new(); set; } +} - /// Exact receipt to inspect, not a server-name guess. - [JsonPropertyName("installationId")] - public string InstallationId { get; set; } = string.Empty; +/// An absolute URL on the identity's GitHub host. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class AccountGetWebUrlResult +{ + /// Absolute URL for the requested path. + [JsonPropertyName("url")] + public string Url { get; set; } = string.Empty; +} - /// Existing selected session on the original connection. - [JsonPropertyName("policySessionId")] - public string PolicySessionId { get; set; } = string.Empty; +/// A path to resolve against the GitHub host an identity is signed in to. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class AccountGetWebUrlRequest +{ + /// Identity whose host the path resolves against. Omit to resolve against the public github.com host. + [JsonPropertyName("identity")] + public AuthIdentity? Identity { get; set; } + + /// Host-relative path, without a leading slash, for example `settings/copilot/features`. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; } -/// One-use application of the exact retained removal plan. +/// The environment variable supplying a classic personal access token, when one is set. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpApplyUninstallRequest +internal sealed class AccountGetTokenEnvVarResult { - /// Required authenticated bound installation capabilities. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); set; } + /// Variable name, for example `GH_TOKEN`. Absent when no classic token is present in the environment. + [JsonPropertyName("name")] + public string? Name { get; set; } +} - /// Opaque original removal plan, consumed once. - [JsonPropertyName("planHandle")] - public string PlanHandle { get; set; } = string.Empty; +/// Confirmation that the secret values were registered. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SecretsAddFilterValuesResult +{ + /// Whether the values were successfully registered. + [JsonPropertyName("ok")] + public bool Ok { get; set; } +} - /// Same existing selected session as removal preparation. - [JsonPropertyName("policySessionId")] - public string PolicySessionId { get; set; } = string.Empty; +/// Secret values to add to the redaction filter. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SecretsAddFilterValuesRequest +{ + /// Raw secret values to register for redaction. + [JsonPropertyName("values")] + public IList Values { get => field ??= []; set; } } -/// User-configured MCP servers, keyed by server name. +/// Concrete configuration file containing an MCP server declaration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpConfigList +public sealed class McpSourceFile { - /// All MCP servers from user config, keyed by name. - [JsonPropertyName("servers")] - public IDictionary Servers { get => field ??= new Dictionary(); set; } + /// RFC 6901 JSON Pointer to the server declaration, when known. + [JsonPropertyName("jsonPointer")] + public string? JsonPointer { get; set; } + + /// Canonical file URI for the configuration document. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("uri")] + public string Uri { get; set; } = string.Empty; } -/// MCP server name and configuration to add to user configuration. +/// Plugin identity associated with an MCP server declaration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpConfigAddRequest +public sealed class McpSourcePlugin { - /// MCP server configuration (stdio process or remote HTTP/SSE). - [JsonPropertyName("config")] - public JsonElement Config { get; set; } + /// Canonical plugin identity. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// Unique name for the MCP server. - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + /// Human-readable plugin name, when available. [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] [MinLength(1)] [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + public string? Name { get; set; } + + /// Plugin version, when available. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("version")] + public string? Version { get; set; } } -/// MCP server name and replacement configuration to write to user configuration. +/// Canonical identity and location of the effective MCP server declaration. The declaration is uniquely addressed by this source id together with the discovered server name. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpConfigUpdateRequest +public sealed class McpSourceRef { - /// MCP server configuration (stdio process or remote HTTP/SSE). - [JsonPropertyName("config")] - public JsonElement Config { get; set; } + /// Open semantic editability identifier. Known values are editable and read-only. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("editability")] + public string Editability { get; set; } = string.Empty; - /// Name of the MCP server to update. - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + /// Configuration file location, when the declaration is file-backed. + [JsonPropertyName("file")] + public McpSourceFile? File { get; set; } + + /// Opaque stable identity for the configuration source. Clients must not parse this value. [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] [MinLength(1)] - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Open source-kind identifier. Known values include user, workspace, invocation, plugin, builtin, and device-registry. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("kind")] + public string Kind { get; set; } = string.Empty; + + /// Plugin identity, when the declaration is plugin-provided. + [JsonPropertyName("plugin")] + public McpSourcePlugin? Plugin { get; set; } } -/// MCP server name to remove from user configuration. +/// MCP server discovered by `mcp.discover`, with config source, optional plugin source, transport type, and enabled state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpConfigRemoveRequest +public sealed class DiscoveredMcpServer { - /// OAuth Client ID Metadata Document URL whose persisted credentials should also be removed. - [JsonPropertyName("authClientIdMetadataUrl")] - public string? AuthClientIdMetadataUrl { get; set; } + /// Canonical identity and location of the effective server declaration. + [JsonPropertyName("effectiveSource")] + public McpSourceRef? EffectiveSource { get; set; } - /// Name of the MCP server to remove. + /// Whether the server is enabled (not in the disabled list). + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } + + /// Server name (config key). [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] [MinLength(1)] [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; -} -/// MCP server names to enable for new sessions. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpConfigEnableRequest -{ - /// Names of MCP servers to enable. Each server is removed from the persisted disabled list so new sessions spawn it. Unknown or already-enabled names are ignored. - [JsonPropertyName("names")] - public IList Names { get => field ??= []; set; } + /// Configuration source: user, workspace, plugin, or builtin. + [JsonPropertyName("source")] + public McpServerSource Source { get; set; } + + /// Plugin name that provided this server, when source is plugin. + [JsonPropertyName("sourcePlugin")] + public string? SourcePlugin { get; set; } + + /// Plugin version that provided this server, when source is plugin. + [JsonPropertyName("sourcePluginVersion")] + public string? SourcePluginVersion { get; set; } + + /// Server transport type: stdio, http, sse (deprecated), or memory. + [JsonPropertyName("type")] + public DiscoveredMcpServerType? Type { get; set; } } -/// MCP server names to disable for new sessions. +/// MCP servers discovered from user, workspace, plugin, and built-in sources. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpConfigDisableRequest +public sealed class McpDiscoverResult { - /// Names of MCP servers to disable. Each server is added to the persisted disabled list so new sessions skip it. Already-disabled names are ignored. Active sessions keep their current connections until they end. - [JsonPropertyName("names")] - public IList Names { get => field ??= []; set; } + /// MCP servers discovered from all sources. + [JsonPropertyName("servers")] + public IList Servers { get => field ??= []; set; } } -/// New-work inventory or recovery request under an explicitly selected existing session. +/// Optional working directory used as context for MCP server discovery. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpInstallationsRequest +internal sealed class McpDiscoverRequest { - /// Required authenticated bound installation contract. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); set; } + /// Whether to include canonical effectiveSource metadata for each discovered server. Callers must opt in so protocol-3 clients retain the legacy closed response shape. + [JsonPropertyName("includeEffectiveSource")] + public bool? IncludeEffectiveSource { get; set; } - /// Existing selected local session on this connection. - [JsonPropertyName("policySessionId")] - public string PolicySessionId { get; set; } = string.Empty; + /// Working directory used as context for discovery (e.g., plugin resolution). + [JsonPropertyName("workingDirectory")] + public string? WorkingDirectory { get; set; } } -/// Existing-operation control. A new session selector is deliberately not accepted. +/// Names of the MCP servers the runtime supplies itself. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpInstallationOperationRequest +internal sealed class McpListBuiltinServerNamesResult { - /// Original installation wire capability; new-work authentication is not reacquired. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); set; } - - /// Exact runtime-issued operation ID on the original connection. - [JsonPropertyName("operationId")] - public string OperationId { get; set; } = string.Empty; + /// Every built-in server name. A host disabling the built-ins wholesale suppresses exactly this set. + [JsonPropertyName("names")] + public IList Names { get => field ??= []; set; } } -/// Installed plugin that contributes a discovered extension. +/// Whether the gh-replaceable GitHub MCP tools may be clipped for the session described by the request. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DiscoveredExtensionPlugin +internal sealed class McpShouldExcludeGitHubToolsResult { - /// Installed plugin name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// True only when both halves hold: the session's filters still reach the platform shell tool, and the host actually has the `gh` those tools would be replaced by. Feed it straight back as the `excludeGhReplaceableTools` build option. + [JsonPropertyName("excludeGhReplaceableTools")] + public bool ExcludeGhReplaceableTools { get; set; } } -/// Discovered extension metadata and persistent enablement state. +/// The session tool filters that decide whether the session still has a shell to run `gh` with. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DiscoveredExtension +internal sealed class McpShouldExcludeGitHubToolsRequest { - /// Whether this extension's persistent per-ID preference is enabled. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } + /// The session's tool allowlist, when it set one. Omitted means the session constrains nothing this way. + [JsonPropertyName("availableTools")] + public IList? AvailableTools { get; set; } - /// Source-qualified ID accepted by both server and session extension enablement methods. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// The session's tool denylist, when it set one. Omitted means the session constrains nothing this way. + [JsonPropertyName("excludedTools")] + public IList? ExcludedTools { get; set; } - /// Human-readable extension name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// How the allowlist and denylist combine when both are set. Omitted means the default every session gets, so a caller that never chose a precedence is answered as its sessions behave. + [JsonPropertyName("toolFilterPrecedence")] + public OptionsUpdateToolFilterPrecedence? ToolFilterPrecedence { get; set; } +} - /// Absolute path to the extension entry module, suitable for revealing it in a file manager. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; +/// Outcome of an mcp.planInstall call: either a normalised plan, or one typed refusal. Nothing is written in either case. +/// Polymorphic base type discriminated by kind. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(McpPlanInstallResultPlanned), "planned")] +[JsonDerivedType(typeof(McpPlanInstallResultNegotiationRefused), "negotiation-refused")] +[JsonDerivedType(typeof(McpPlanInstallResultHandleRejected), "handle-rejected")] +[JsonDerivedType(typeof(McpPlanInstallResultInvalidRequest), "invalid-request")] +[JsonDerivedType(typeof(McpPlanInstallResultAuthenticationRequired), "authentication-required")] +[JsonDerivedType(typeof(McpPlanInstallResultPolicyRejected), "policy-rejected")] +[JsonDerivedType(typeof(McpPlanInstallResultNetworkFailure), "network-failure")] +[JsonDerivedType(typeof(McpPlanInstallResultUnsafeRetrieval), "unsafe-retrieval")] +[JsonDerivedType(typeof(McpPlanInstallResultMalformedCard), "malformed-card")] +[JsonDerivedType(typeof(McpPlanInstallResultContractViolation), "contract-violation")] +[JsonDerivedType(typeof(McpPlanInstallResultUnavailableTransport), "unavailable-transport")] +[JsonDerivedType(typeof(McpPlanInstallResultNotInstallable), "not-installable")] +[JsonDerivedType(typeof(McpPlanInstallResultUnavailable), "unavailable")] +public partial class McpPlanInstallResult +{ + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// Containing plugin metadata for plugin-contributed extensions. - [JsonPropertyName("plugin")] - public DiscoveredExtensionPlugin? Plugin { get; set; } - /// Discovery source. - [JsonPropertyName("source")] - public DiscoveredExtensionSource Source { get; set; } +/// The protocol version and capability set the runtime actually honoured for a successful catalog operation. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class CatalogNegotiatedContract +{ + /// Wire features the runtime understood for this operation. Includes the five original catalog capabilities and only explicitly requested supported additions, in supported order without duplicates. Capabilities that introduce new success-union variants or operations are therefore included only when explicitly required, preserving older protocol-v3 clients. Always a superset of the caller's required features, because any shortfall is a refusal instead. Operation availability remains a separate typed result. + [JsonPropertyName("grantedCapabilities")] + public IList GrantedCapabilities { get => field ??= []; set; } + + /// Protocol version of the runtime that served the request. + [JsonPropertyName("runtimeProtocolVersion")] + public long RuntimeProtocolVersion { get; set; } } -/// Extensions discovered from persisted Copilot home state and their effective loading mode. Launch-scoped additional plugins are not included. +/// The configuration-change alternative for the transportChoices entry at the same index. Only the selected alternative is applied; entries are not cumulative. The payload stays behind the runtime boundary. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DiscoveredExtensions +public sealed class McpPlanConfigurationChange { - /// Discovered user and enabled installed-plugin extensions from persisted Copilot home state. - [JsonPropertyName("extensions")] - public IList Extensions { get => field ??= []; set; } + /// Names of the configuration fields the change would set, without their values. + [JsonPropertyName("changedFields")] + public IList ChangedFields { get => field ??= []; set; } - /// Effective extension loading mode. Defaults to load_and_augment when unset. - [JsonPropertyName("mode")] - public DiscoveredExtensionMode Mode { get; set; } + /// Configuration key the change applies to. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("configKey")] + public string ConfigKey { get; set; } = string.Empty; + + /// Whether the change would create a new entry or modify an existing one. + [JsonPropertyName("operation")] + public McpPlanConfigurationOperation Operation { get; set; } + + /// Scope the change would be written to. + [JsonPropertyName("scope")] + public McpPlanScope Scope { get; set; } + + /// Secret placeholders the written configuration would reference. The constrained placeholder type cannot carry a literal secret value. + [JsonPropertyName("secretReferences")] + public IList SecretReferences { get => field ??= []; set; } } -/// Source-qualified extension identifiers to persistently enable for future sessions. +/// Normalised identity of the MCP server a plan targets, independent of how the card spelled it. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class DiscoveredExtensionsEnableRequest +public sealed class McpPlanResourceIdentity { - /// Source-qualified user or plugin extension IDs to enable. - [JsonPropertyName("ids")] - public IList Ids { get => field ??= []; set; } + /// Canonical, normalised name of the server, for example `io.github.owner/server`. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("canonicalName")] + public string CanonicalName { get; set; } = string.Empty; + + /// Registry identifier of the server, when it came from a registry. + [JsonPropertyName("registryId")] + public string? RegistryId { get; set; } + + /// Local configuration key the server would be recorded under. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; + + /// Version advertised by the card, when it declares one. + [JsonPropertyName("version")] + public string? Version { get; set; } } -/// Source-qualified extension identifiers to persistently disable for future sessions. +/// Outcome of evaluating the planned server against registry and enterprise policy. Evaluation is read-only. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class DiscoveredExtensionsDisableRequest +public sealed class McpPlanPolicyResult { - /// Source-qualified user or plugin extension IDs to disable. - [JsonPropertyName("ids")] - public IList Ids { get => field ??= []; set; } + /// What policy decided for this server. + [JsonPropertyName("decision")] + public McpPlanPolicyDecision Decision { get; set; } + + /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("reason")] + public string? Reason { get; set; } + + /// Which authority produced the decision. + [JsonPropertyName("source")] + public McpPlanPolicySource Source { get; set; } } -/// Skill installation management result with the honoured contract, or a typed refusal. -/// Polymorphic base type discriminated by kind. +/// Semantic digest of a strictly parsed and schema-validated JSON MCP card. Both URL-backed and embedded cards are canonicalised with RFC 8785 JSON Canonicalization Scheme, encoded as UTF-8, and hashed with SHA-256. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(SkillInstallationManagementResultOutcome), "outcome")] -[JsonDerivedType(typeof(SkillInstallationManagementResultNegotiationRefused), "negotiation-refused")] -[JsonDerivedType(typeof(SkillInstallationManagementResultInvalidRequest), "invalid-request")] -public partial class SkillInstallationManagementResult +public sealed class CardDigest { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// Digest algorithm and canonical representation. + [JsonPropertyName("algorithm")] + public CardDigestAlgorithm Algorithm { get; set; } + /// SHA-256 digest of the RFC 8785 canonical UTF-8 bytes, encoded as exactly 64 lowercase hexadecimal characters. + [RegularExpression("^[0-9a-f]{64}$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(64)] + [MaxLength(64)] + [JsonPropertyName("value")] + public string Value { get; set; } = string.Empty; +} -/// Management outcome for verified Skill inventory, planning and removal. -/// Polymorphic base type discriminated by kind. +/// Provenance of the exact validated JSON MCP card content bound privately to a completed plan and its opaque handle. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(SkillInstallationManagementOutcomeRecoveryRequired), "recovery-required")] -[JsonDerivedType(typeof(SkillInstallationManagementOutcomeInstallPlanned), "install-planned")] -[JsonDerivedType(typeof(SkillInstallationManagementOutcomeListed), "listed")] -[JsonDerivedType(typeof(SkillInstallationManagementOutcomeRecovered), "recovered")] -[JsonDerivedType(typeof(SkillInstallationManagementOutcomeRolledBack), "rolled-back")] -[JsonDerivedType(typeof(SkillInstallationManagementOutcomeUninstallPlanned), "uninstall-planned")] -[JsonDerivedType(typeof(SkillInstallationManagementOutcomeOperation), "operation")] -[JsonDerivedType(typeof(SkillInstallationManagementOutcomeEnabledChanged), "enabled-changed")] -[JsonDerivedType(typeof(SkillInstallationManagementOutcomeRefused), "refused")] -public partial class SkillInstallationManagementOutcome +public sealed class McpPlanProvenance { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// Authority associated with the validated card, without path, query, or credentials. Inert untrusted data. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("authority")] + public string Authority { get; set; } = string.Empty; + + /// Semantic digest of the exact validated JSON content bound to the plan handle. + [JsonPropertyName("cardDigest")] + public CardDigest CardDigest { get => field ??= new(); set; } + /// JSON MCP media type the validated card was interpreted as. + [JsonPropertyName("mediaType")] + public McpServerCardMediaType MediaType { get; set; } -/// Already-confirmed durable work must be reconciled before new mutations or inventory. -/// The recovery-required variant of . + /// ISO 8601 timestamp at which the runtime completed strict parsing and schema validation of the card content. + [JsonPropertyName("validatedAt")] + public string ValidatedAt { get; set; } = string.Empty; +} + +/// Where a plan would be written. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationManagementOutcomeRecoveryRequired : SkillInstallationManagementOutcome +public sealed class McpPlanTarget { - /// - [JsonIgnore] - public override string Kind => "recovery-required"; + /// Configuration key the server would be recorded under within that scope. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("configKey")] + public string ConfigKey { get; set; } = string.Empty; + + /// Configuration scope the plan targets. + [JsonPropertyName("scope")] + public McpPlanScope Scope { get; set; } } -/// Safe verified Skill review fields. No raw credential, candidate handle or plan handle. -/// Polymorphic base type discriminated by action. +/// One eligible way to run the server, represented as a tagged package or remote variant so package identity and endpoint states cannot contradict the install method. +/// Polymorphic base type discriminated by installMethod. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonPolymorphic( - TypeDiscriminatorPropertyName = "action", + TypeDiscriminatorPropertyName = "installMethod", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(SkillInstallationReviewInstall), "install")] -[JsonDerivedType(typeof(SkillInstallationReviewUninstall), "uninstall")] -public partial class SkillInstallationReview +[JsonDerivedType(typeof(McpPlanTransportChoicePackage), "package")] +[JsonDerivedType(typeof(McpPlanTransportChoiceRemote), "remote")] +public partial class McpPlanTransportChoice { /// The type discriminator. - [JsonPropertyName("action")] - public virtual string Action { get; set; } = string.Empty; + [JsonPropertyName("installMethod")] + public virtual string InstallMethod { get; set; } = string.Empty; } -/// One reviewed Skill file. +/// One non-secret value a transport choice needs, represented as a scalar or enumerated variant so enum values cannot be missing or attached to another type. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillInstallationFileReview +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(McpPlanRequiredValueScalar), "scalar")] +[JsonDerivedType(typeof(McpPlanRequiredValueEnum), "enum")] +public partial class McpPlanRequiredValue { - /// SHA-256 digest of the exact file bytes. - [JsonPropertyName("digest")] - public string Digest { get; set; } = string.Empty; - - /// Whether the file is installed with executable permissions. - [JsonPropertyName("executable")] - public bool Executable { get; set; } - - /// Declared media type for the file. - [JsonPropertyName("mediaType")] - public string MediaType { get; set; } = string.Empty; - - /// Relative file path within the Skill root. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; - - /// Exact reviewed file size in bytes. - [JsonPropertyName("sizeBytes")] - public long SizeBytes { get; set; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// Source identity retained from Agent Finder and the pinned GitHub descriptor. + +/// One non-secret scalar value a transport choice needs before it can be applied. +/// The scalar variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillInstallationSource +public partial class McpPlanRequiredValueScalar : McpPlanRequiredValue { - /// Digest of the descriptor's bundle manifest. - [JsonPropertyName("bundleDigest")] - public string BundleDigest { get; set; } = string.Empty; - - /// Agent Finder materialisation revision identifier. - [JsonPropertyName("catalogRevisionId")] - public string CatalogRevisionId { get; set; } = string.Empty; - - /// Digest of the canonical materialisation descriptor. - [JsonPropertyName("descriptorDigest")] - public string DescriptorDigest { get; set; } = string.Empty; - - /// Repository full name, for example owner/name. - [JsonPropertyName("repository")] - public string Repository { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "scalar"; - /// GitHub repository database identifier. - [JsonPropertyName("repositoryId")] - public string RepositoryId { get; set; } = string.Empty; + /// Where the value is applied when the server is launched. + [JsonPropertyName("category")] + public required McpPlanValueCategory Category { get; set; } - /// Agent Finder resource identifier. - [JsonPropertyName("resourceId")] - public string ResourceId { get; set; } = string.Empty; + /// Default supplied by the card, when the value can be resolved without input. Presence is the authoritative indication that a default exists. Inert untrusted data. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("defaultValue")] + public string? DefaultValue { get; set; } - /// Pinned Git commit revision. - [JsonPropertyName("revision")] - public string Revision { get; set; } = string.Empty; + /// Human-readable explanation from the card. Inert untrusted text. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("description")] + public string? Description { get; set; } - /// Root path within the pinned repository. - [JsonPropertyName("root")] - public string Root { get; set; } = string.Empty; -} + /// Whether the value may be supplied more than once. + [JsonPropertyName("isRepeated")] + public required bool IsRepeated { get; set; } -/// A user-facing personal Skill installation location without absolute host paths. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillInstallationLocation -{ - /// Diagnostics-only absolute host path. Hosts must not display it by default. - [JsonPropertyName("diagnosticsAbsolutePath")] - public string? DiagnosticsAbsolutePath { get; set; } + /// Key the value is supplied under. Inert untrusted data. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("key")] + public required string Key { get; set; } - /// Safe display label, for example ~/.copilot/skills/run-checks. - [JsonPropertyName("displayLabel")] - public string DisplayLabel { get; set; } = string.Empty; + /// Whether the value must be present for the plan to be applicable. + [JsonPropertyName("required")] + public required bool Required { get; set; } - /// Path relative to the Copilot home. - [JsonPropertyName("relativePath")] - public string RelativePath { get; set; } = string.Empty; + /// Human-readable label from the card. Inert untrusted text. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(200)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("title")] + public string? Title { get; set; } - /// Installation scope. Agent Finder Skills are installed in the user's personal Copilot home. - [JsonPropertyName("scope")] - public SkillInstallationScope Scope { get; set; } + /// Scalar type the value must conform to. + [JsonPropertyName("valueType")] + public required McpPlanScalarValueType ValueType { get; set; } } -/// Review for installing a verified Skill. -/// The install variant of . +/// One enumerated non-secret value a transport choice needs before it can be applied. The permitted values are structurally required. +/// The enum variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationReviewInstall : SkillInstallationReview +public partial class McpPlanRequiredValueEnum : McpPlanRequiredValue { /// [JsonIgnore] - public override string Action => "install"; + public override string Kind => "enum"; - /// Catalogue identity retained from the bound candidate before consent. - [JsonPropertyName("catalogue")] - public required InstallationCatalogueIdentity Catalogue { get; set; } + /// Where the value is applied when the server is launched. + [JsonPropertyName("category")] + public required McpPlanValueCategory Category { get; set; } - /// Skill description from SKILL.md when present. + /// Default supplied by the card, when the value can be resolved without input. Presence is the authoritative indication that a default exists. Inert untrusted data. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("defaultValue")] + public string? DefaultValue { get; set; } + + /// Human-readable explanation from the card. Inert untrusted text. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("description")] public string? Description { get; set; } - /// - /// Complete verified SKILL.md content. Planning refuses with review-too-large - /// when this exceeds 262144 UTF-8 bytes; it is never truncated. - /// - [JsonPropertyName("entrypointContent")] - public required string EntrypointContent { get; set; } - - /// Relative path of the verified Skill entrypoint. - [JsonPropertyName("entrypointPath")] - public required string EntrypointPath { get; set; } - - /// Reviewed files and digests. - [JsonPropertyName("files")] - public required IList Files { get; set; } + /// Non-empty permitted value set. Inert untrusted data. + [JsonPropertyName("enumValues")] + public required IList EnumValues { get; set; } - /// Installing never grants immediate use; the Skill is written disabled. - [JsonPropertyName("installsDisabled")] - public required bool InstallsDisabled { get; set; } + /// Whether the value may be supplied more than once. + [JsonPropertyName("isRepeated")] + public required bool IsRepeated { get; set; } - /// Skill invocation name from SKILL.md. - [JsonPropertyName("name")] - public required string Name { get; set; } + /// Key the value is supplied under. Inert untrusted data. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("key")] + public required string Key { get; set; } - /// Exact verified source identity. - [JsonPropertyName("source")] - public required SkillInstallationSource Source { get; set; } + /// Whether the value must be present for the plan to be applicable. + [JsonPropertyName("required")] + public required bool Required { get; set; } - /// Exact user-scope target location without an absolute host path. - [JsonPropertyName("target")] - public required SkillInstallationLocation Target { get; set; } + /// Human-readable label from the card. Inert untrusted text. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(200)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("title")] + public string? Title { get; set; } - /// Total reviewed payload size in bytes. - [JsonPropertyName("totalBytes")] - public required long TotalBytes { get; set; } + /// Discriminator: the value must be one of `enumValues`. + [JsonPropertyName("valueType")] + public required McpPlanEnumValueType ValueType { get; set; } } -/// Durable verified Skill ownership summary. +/// A secret a transport choice needs, referenced by placeholder. No secret value ever appears in a plan, and the placeholder resolves against the keychain only when a plan is applied. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillInstallationSummary +public sealed class McpPlanSecretPlaceholder { - /// Catalogue identity retained at install time. - [JsonPropertyName("catalogue")] - public InstallationCatalogueIdentity Catalogue { get => field ??= new(); set; } + /// Key the secret is supplied under. Inert untrusted data. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("key")] + public string Key { get; set; } = string.Empty; - /// Persisted enablement requested for this installation. - [JsonPropertyName("configuredEnabled")] - public bool ConfiguredEnabled { get; set; } + /// The runtime-assigned `${secret:<id>}` placeholder written into configuration in place of the value. + [JsonPropertyName("placeholder")] + public string Placeholder { get; set; } = string.Empty; - /// Exact durable installation receipt identity. - [JsonPropertyName("installationId")] - public string InstallationId { get; set; } = string.Empty; + /// Human-readable label from the card. Inert untrusted text. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(200)] + [JsonPropertyName("title")] + public string? Title { get; set; } +} - /// ISO 8601 wall-clock installation time. - [JsonPropertyName("installedAt")] - public string InstalledAt { get; set; } = string.Empty; +/// An eligible local-package transport choice. Package identity is required and a remote endpoint cannot be represented. +/// The package variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class McpPlanTransportChoicePackage : McpPlanTransportChoice +{ + /// + [JsonIgnore] + public override string InstallMethod => "package"; - /// Skill invocation name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Stable identifier for this choice within the plan, used to select it when the plan is applied. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(128)] + [JsonPropertyName("choiceId")] + public required string ChoiceId { get; set; } - /// Operation that installed this Skill. - [JsonPropertyName("operationId")] - public string OperationId { get; set; } = string.Empty; + /// Package identifier. Inert untrusted data. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(512)] + [JsonPropertyName("packageIdentifier")] + public required string PackageIdentifier { get; set; } - /// Ownership state observed from files and receipts. - [JsonPropertyName("ownershipState")] - public SkillInstallationOwnershipState OwnershipState { get; set; } + /// Packaging ecosystem, for example `oci` or `npm`. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(64)] + [JsonPropertyName("packageType")] + public required string PackageType { get; set; } - /// Bound-session load observation. - [JsonPropertyName("sessionState")] - public SkillInstallationSessionState SessionState { get; set; } + /// Typed values this choice requires, excluding secrets. + [JsonPropertyName("requiredValues")] + public required IList RequiredValues { get; set; } - /// Exact retained verified source identity. - [JsonPropertyName("source")] - public SkillInstallationSource Source { get => field ??= new(); set; } + /// Secrets this choice requires, referenced by placeholder only. + [JsonPropertyName("secretPlaceholders")] + public required IList SecretPlaceholders { get; set; } - /// User-facing installation location without an absolute host path. - [JsonPropertyName("target")] - public SkillInstallationLocation Target { get => field ??= new(); set; } + /// Local process transport this package choice would use. + [JsonPropertyName("transport")] + public required McpPlanPackageTransport Transport { get; set; } } -/// Review for uninstalling an owned verified Skill. -/// The uninstall variant of . +/// An eligible remote-endpoint transport choice. The endpoint is required and package identity cannot be represented. +/// The remote variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationReviewUninstall : SkillInstallationReview +public partial class McpPlanTransportChoiceRemote : McpPlanTransportChoice { /// [JsonIgnore] - public override string Action => "uninstall"; + public override string InstallMethod => "remote"; - /// Catalogue identity retained at install time. - [JsonPropertyName("catalogue")] - public required InstallationCatalogueIdentity Catalogue { get; set; } + /// Stable identifier for this choice within the plan, used to select it when the plan is applied. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(128)] + [JsonPropertyName("choiceId")] + public required string ChoiceId { get; set; } - /// Files recorded by the installation receipt. - [JsonPropertyName("files")] - public required IList Files { get; set; } + /// Endpoint URL. Inert untrusted data. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(2048)] + [JsonPropertyName("endpoint")] + public required string Endpoint { get; set; } - /// Whether current files differ from the receipt. Apply refuses drift. - [JsonPropertyName("filesModified")] - public required bool FilesModified { get; set; } + /// Typed values this choice requires, excluding secrets. + [JsonPropertyName("requiredValues")] + public required IList RequiredValues { get; set; } - /// Owned installation being removed. - [JsonPropertyName("installation")] - public required SkillInstallationSummary Installation { get; set; } + /// Secrets this choice requires, referenced by placeholder only. + [JsonPropertyName("secretPlaceholders")] + public required IList SecretPlaceholders { get; set; } - /// Total receipt-owned payload size in bytes. - [JsonPropertyName("totalBytes")] - public required long TotalBytes { get; set; } + /// Endpoint transport this remote choice would use. + [JsonPropertyName("transport")] + public required McpPlanRemoteTransport Transport { get; set; } } -/// A computed Skill install plan. Nothing has been applied. +/// A normalised, inert description of what installing an MCP server would involve. Carries no raw card, no install specification, and no secret value. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillInstallPlan +public sealed class McpInstallPlan { - /// Original wall-clock expiry as an ISO 8601 timestamp. - [JsonPropertyName("expiresAt")] - public string ExpiresAt { get; set; } = string.Empty; + /// Alternative configuration changes, with exactly one entry for each transportChoices entry in the same order. Only the entry for the subsequently selected transport applies; these are not cumulative writes. Payloads remain behind the runtime boundary. + [JsonPropertyName("configurationChanges")] + public IList ConfigurationChanges { get => field ??= []; set; } - /// Original operation identifier returned before confirmation. - [JsonPropertyName("operationId")] - public string OperationId { get; set; } = string.Empty; + /// Normalised identity of the server the plan would install. + [JsonPropertyName("identity")] + public McpPlanResourceIdentity Identity { get => field ??= new(); set; } - /// One-use plan handle, bound to the original candidate authority. + /// Opaque, runtime-instance scoped, TTL-bound, single-use handle for this plan. Rejected when stale, replayed, or presented to a different runtime instance. Never logged. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] [JsonPropertyName("planHandle")] public string PlanHandle { get; set; } = string.Empty; - /// Safe review to present before applying the plan. - [JsonPropertyName("review")] - public SkillInstallationReview Review { get => field ??= new(); set; } -} + /// ISO 8601 timestamp after which the plan handle is stale and will be rejected. Abandoning a plan needs no call: an unused handle simply expires, so cancellation before commit is side-effect free. + [JsonPropertyName("planHandleExpiresAt")] + public string PlanHandleExpiresAt { get; set; } = string.Empty; -/// A verified Skill installation plan was prepared. -/// The install-planned variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationManagementOutcomeInstallPlanned : SkillInstallationManagementOutcome -{ - /// - [JsonIgnore] - public override string Kind => "install-planned"; + /// Outcome of evaluating the server against registry and enterprise policy. + [JsonPropertyName("policy")] + public McpPlanPolicyResult Policy { get => field ??= new(); set; } - /// Prepared install plan. - [JsonPropertyName("plan")] - public required SkillInstallPlan Plan { get; set; } -} + /// Origin and semantic digest of the exact validated JSON MCP card content bound to this plan. + [JsonPropertyName("provenance")] + public McpPlanProvenance Provenance { get => field ??= new(); set; } -/// Owned Skill installations were listed. -/// The listed variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationManagementOutcomeListed : SkillInstallationManagementOutcome -{ - /// - [JsonIgnore] - public override string Kind => "listed"; + /// Identifier of the choice the runtime would pick by default. Omitted when there is no eligible transport, or when the runtime expresses no preference. + [JsonPropertyName("recommendedTransportChoiceId")] + public string? RecommendedTransportChoiceId { get; set; } - /// Owned Skill installation summaries. - [JsonPropertyName("installations")] - public required IList Installations { get; set; } + /// Whether applying this plan would require an MCP reload to take effect. Planning itself never reloads. + [JsonPropertyName("reloadRequired")] + public bool ReloadRequired { get; set; } + + /// True only when every eligible transport choice needs additional values or secrets. False means at least one choice needs no additional configuration, not that every choice is ready. A later apply operation must validate the selected choice's own inputs, secrets and policy after explicit confirmation. + [JsonPropertyName("requiresInteractiveConfiguration")] + public bool RequiresInteractiveConfiguration { get; set; } + + /// Configuration scope and key the plan would write to. + [JsonPropertyName("target")] + public McpPlanTarget Target { get => field ??= new(); set; } + + /// Every eligible transport, so a host can present an explicit choice. A completed plan always has at least one; when none is eligible, planning returns `CatalogUnavailableTransportError` instead. + [JsonPropertyName("transportChoices")] + public IList TransportChoices { get => field ??= []; set; } } -/// Recovery completed and inventory was inspected. -/// The recovered variant of . +/// A computed MCP install plan. Nothing has been applied: the plan describes what installing would change, and the plan handle is what a later apply operation would consume. +/// The planned variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationManagementOutcomeRecovered : SkillInstallationManagementOutcome +public partial class McpPlanInstallResultPlanned : McpPlanInstallResult { /// [JsonIgnore] - public override string Kind => "recovered"; + public override string Kind => "planned"; - /// Owned Skill installation summaries after recovery. - [JsonPropertyName("installations")] - public required IList Installations { get; set; } + /// Protocol version and capabilities the runtime honoured. + [JsonPropertyName("negotiated")] + public required CatalogNegotiatedContract Negotiated { get; set; } + + /// The normalised plan. + [JsonPropertyName("plan")] + public required McpInstallPlan Plan { get; set; } } -/// Interrupted work was safely compensated and the pending marker was cleared. -/// The rolled-back variant of . +/// The caller's protocol version or required capabilities cannot be honoured. Returned instead of a partial or ambiguous success. +/// The negotiation-refused variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationManagementOutcomeRolledBack : SkillInstallationManagementOutcome +public partial class McpPlanInstallResultNegotiationRefused : McpPlanInstallResult { /// [JsonIgnore] - public override string Kind => "rolled-back"; + public override string Kind => "negotiation-refused"; - /// Original operation identity. - [JsonPropertyName("operation_id")] - public required string OperationId { get; set; } + /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } - /// Cause of the compensation. + /// Lowest caller protocol version this runtime will serve. + [JsonPropertyName("minimumSupportedProtocolVersion")] + public required long MinimumSupportedProtocolVersion { get; set; } + + /// Whether the version or the capability set was the problem. [JsonPropertyName("reason")] - public required SkillInstallationFailureReason Reason { get; set; } + public required CatalogNegotiationRefusedReason Reason { get; set; } + + /// Protocol version of the runtime that refused the request. + [JsonPropertyName("runtimeProtocolVersion")] + public required long RuntimeProtocolVersion { get; set; } + + /// Capabilities this runtime can safely advertise to this caller. The complete five-capability protocol-3 legacy set is always present; every capability added after that baseline appears only when the caller required it, so an older closed-enum decoder can still consume a refusal. This list does not imply that every deployment has enabled every operation. + [JsonPropertyName("supportedCapabilities")] + public required IList SupportedCapabilities { get; set; } + + /// The subset of the caller's bounded extensible capability identifiers this runtime cannot honour. + [JsonPropertyName("unsupportedCapabilities")] + public required IList UnsupportedCapabilities { get; set; } } -/// A computed Skill uninstall plan. Nothing has been removed. +/// A presented handle was not accepted. Handles are runtime-instance scoped, TTL-bound, and single-use, so each way of failing is reported distinctly. +/// The handle-rejected variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillUninstallPlan +public partial class McpPlanInstallResultHandleRejected : McpPlanInstallResult { - /// Original wall-clock expiry as an ISO 8601 timestamp. - [JsonPropertyName("expiresAt")] - public string ExpiresAt { get; set; } = string.Empty; - - /// Owned Skill installation being removed. - [JsonPropertyName("installation")] - public SkillInstallationSummary Installation { get => field ??= new(); set; } + /// + [JsonIgnore] + public override string Kind => "handle-rejected"; - /// Original removal operation identifier returned before confirmation. - [JsonPropertyName("operationId")] - public string OperationId { get; set; } = string.Empty; + /// Which kind of handle was presented. + [JsonPropertyName("handleType")] + public required CatalogHandleType HandleType { get; set; } - /// One-use uninstall plan handle. - [JsonPropertyName("planHandle")] - public string PlanHandle { get; set; } = string.Empty; + /// Human-readable explanation, safe to surface. Never contains the handle itself, nor a query, URL, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } - /// Safe review to present before applying removal. - [JsonPropertyName("review")] - public SkillInstallationReview Review { get => field ??= new(); set; } + /// Why the handle was rejected. + [JsonPropertyName("reason")] + public required CatalogHandleRejectionReason Reason { get; set; } } -/// An owned Skill uninstall plan was prepared. -/// The uninstall-planned variant of . +/// The request was rejected because a bounded field fell outside its permitted range or a required field was unusable. Pagination may also be rejected by the authority after a continuation request; repeat the search without page. +/// The invalid-request variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationManagementOutcomeUninstallPlanned : SkillInstallationManagementOutcome +public partial class McpPlanInstallResultInvalidRequest : McpPlanInstallResult { /// [JsonIgnore] - public override string Kind => "uninstall-planned"; + public override string Kind => "invalid-request"; - /// Prepared uninstall plan. - [JsonPropertyName("plan")] - public required SkillUninstallPlan Plan { get; set; } -} + /// Which request field was rejected. + [JsonPropertyName("field")] + public required CatalogInvalidRequestField Field { get; set; } -/// Status snapshot from the original connection, independent of new-work account availability. -/// Polymorphic base type discriminated by phase. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "phase", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(SkillInstallationOperationStatusPreparing), "preparing")] -[JsonDerivedType(typeof(SkillInstallationOperationStatusPrepared), "prepared")] -[JsonDerivedType(typeof(SkillInstallationOperationStatusAwaitingConfirmation), "awaiting-confirmation")] -[JsonDerivedType(typeof(SkillInstallationOperationStatusRevalidating), "revalidating")] -[JsonDerivedType(typeof(SkillInstallationOperationStatusApplying), "applying")] -[JsonDerivedType(typeof(SkillInstallationOperationStatusCompleted), "completed")] -public partial class SkillInstallationOperationStatus -{ - /// The type discriminator. - [JsonPropertyName("phase")] - public virtual string Phase { get; set; } = string.Empty; + /// Human-readable explanation, safe to surface. Never echoes the offending value, nor a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } } - -/// Original operation progress discriminator. -/// The preparing variant of . +/// An optional catalog authentication exchange did not establish the caller's identity. Anonymous search remains supported; this refusal is reserved for an operation that cannot continue after the attempted exchange. It is distinct from `policy-rejected` and from a network failure, and the reason identifies the recovery action. +/// The authentication-required variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationOperationStatusPreparing : SkillInstallationOperationStatus +public partial class McpPlanInstallResultAuthenticationRequired : McpPlanInstallResult { /// [JsonIgnore] - public override string Phase => "preparing"; + public override string Kind => "authentication-required"; - /// Whether cancellation has been requested. - [JsonPropertyName("cancellationRequested")] - public required bool CancellationRequested { get; set; } + /// Human-readable explanation, safe to surface. Never contains a credential or token, nor a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } - /// Original runtime-issued operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// Why authentication failed. Only an expired credential justifies attempting a silent refresh; an absent or rejected credential requires sign-in. + [JsonPropertyName("reason")] + public required CatalogAuthenticationRequiredReason Reason { get; set; } } -/// Original operation progress discriminator. -/// The prepared variant of . +/// Registry or enterprise policy refused the operation. +/// The policy-rejected variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationOperationStatusPrepared : SkillInstallationOperationStatus +public partial class McpPlanInstallResultPolicyRejected : McpPlanInstallResult { /// [JsonIgnore] - public override string Phase => "prepared"; + public override string Kind => "policy-rejected"; - /// Whether cancellation has been requested. - [JsonPropertyName("cancellationRequested")] - public required bool CancellationRequested { get; set; } + /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } - /// Original runtime-issued operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// Which authority produced the decision. + [JsonPropertyName("source")] + public required McpPlanPolicySource Source { get; set; } } -/// Original operation progress discriminator. -/// The awaiting-confirmation variant of . +/// The runtime could not reach the catalog authority or retrieve a card. Covers being offline as well as transport-level failure. +/// The network-failure variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationOperationStatusAwaitingConfirmation : SkillInstallationOperationStatus +public partial class McpPlanInstallResultNetworkFailure : McpPlanInstallResult { /// [JsonIgnore] - public override string Phase => "awaiting-confirmation"; + public override string Kind => "network-failure"; - /// Whether cancellation has been requested. - [JsonPropertyName("cancellationRequested")] - public required bool CancellationRequested { get; set; } + /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } - /// Original runtime-issued operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// Categorised failure, low cardinality so it can be aggregated without carrying a URL. + [JsonPropertyName("reason")] + public required CatalogNetworkFailureReason Reason { get; set; } + + /// Bounded cooldown in seconds before another catalog request should be attempted, when the authority supplied a numeric Retry-After value or the runtime applied its documented fallback. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("retryAfterSeconds")] + public int? RetryAfterSeconds { get; set; } + + /// HTTP status code, when the failure was a rejected response. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("statusCode")] + public int? StatusCode { get; set; } } -/// Original operation progress discriminator. -/// The revalidating variant of . +/// Retrieval was refused by the runtime's hardened fetch boundary before any request left the process, or before a redirect was followed. +/// The unsafe-retrieval variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationOperationStatusRevalidating : SkillInstallationOperationStatus +public partial class McpPlanInstallResultUnsafeRetrieval : McpPlanInstallResult { /// [JsonIgnore] - public override string Phase => "revalidating"; + public override string Kind => "unsafe-retrieval"; - /// Whether cancellation has been requested. - [JsonPropertyName("cancellationRequested")] - public required bool CancellationRequested { get; set; } + /// Human-readable explanation, safe to surface. Never contains the refused URL, nor a query, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } - /// Original runtime-issued operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// Which control refused the retrieval, low cardinality so it can be aggregated without carrying a URL. + [JsonPropertyName("reason")] + public required CatalogUnsafeRetrievalReason Reason { get; set; } } -/// Original operation progress discriminator. -/// The applying variant of . +/// A card could not be parsed or did not satisfy its declared media type's schema. +/// The malformed-card variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationOperationStatusApplying : SkillInstallationOperationStatus +public partial class McpPlanInstallResultMalformedCard : McpPlanInstallResult { /// [JsonIgnore] - public override string Phase => "applying"; + public override string Kind => "malformed-card"; - /// Whether cancellation has been requested; already-started effects require recovery. - [JsonPropertyName("cancellationRequested")] - public required bool CancellationRequested { get; set; } + /// Media type the card was interpreted as, when it declared one this runtime recognises. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("mediaType")] + public CatalogMediaType? MediaType { get; set; } - /// Original runtime-issued operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } -} + /// Human-readable explanation, safe to surface. Never echoes card content, nor a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } -/// Terminal verified Skill mutation result. -/// Polymorphic base type discriminated by kind. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(SkillInstallationOutcomeInstalled), "installed")] -[JsonDerivedType(typeof(SkillInstallationOutcomeUninstalled), "uninstalled")] -[JsonDerivedType(typeof(SkillInstallationOutcomeRolledBack), "rolled-back")] -[JsonDerivedType(typeof(SkillInstallationOutcomeRecoveryRequired), "recovery-required")] -[JsonDerivedType(typeof(SkillInstallationOutcomeDeclined), "declined")] -[JsonDerivedType(typeof(SkillInstallationOutcomeCancelled), "cancelled")] -[JsonDerivedType(typeof(SkillInstallationOutcomeRefused), "refused")] -public partial class SkillInstallationOutcome -{ - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; + /// How the card failed validation. + [JsonPropertyName("reason")] + public required CatalogMalformedCardReason Reason { get; set; } } - -/// The verified Skill was installed disabled. -/// The installed variant of . +/// An upstream catalog response broke the wire contract. Most importantly, every result must carry exactly one of a URL or embedded data: a result carrying both, or neither, is refused here rather than being guessed at. +/// The contract-violation variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationOutcomeInstalled : SkillInstallationOutcome +public partial class McpPlanInstallResultContractViolation : McpPlanInstallResult { /// [JsonIgnore] - public override string Kind => "installed"; + public override string Kind => "contract-violation"; - /// Durable installed Skill summary. - [JsonPropertyName("installation")] - public required SkillInstallationSummary Installation { get; set; } + /// Human-readable explanation, safe to surface. Never echoes response content, nor a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } + + /// Which rule the response broke. + [JsonPropertyName("reason")] + public required CatalogContractViolationReason Reason { get; set; } } -/// The owned Skill was removed. -/// The uninstalled variant of . +/// No transport this runtime can use is available for the requested server. +/// The unavailable-transport variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationOutcomeUninstalled : SkillInstallationOutcome +public partial class McpPlanInstallResultUnavailableTransport : McpPlanInstallResult { /// [JsonIgnore] - public override string Kind => "uninstalled"; + public override string Kind => "unavailable-transport"; - /// Removed installation identity. - [JsonPropertyName("installationId")] - public required string InstallationId { get; set; } + /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } - /// Original removal operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// Why no transport could be offered. + [JsonPropertyName("reason")] + public required CatalogUnavailableTransportReason Reason { get; set; } } -/// The durable transaction was aborted or fully compensated. -/// The rolled-back variant of . +/// The candidate is discoverable but cannot be installed. `application/ai-skill` resolves here, because it stays searchable while remaining typed non-installable. +/// The not-installable variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationOutcomeRolledBack : SkillInstallationOutcome +public partial class McpPlanInstallResultNotInstallable : McpPlanInstallResult { /// [JsonIgnore] - public override string Kind => "rolled-back"; + public override string Kind => "not-installable"; - /// Original operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } - /// Cause of the fully aborted or compensated operation. + /// Why the candidate cannot be installed. [JsonPropertyName("reason")] - public required SkillInstallationFailureReason Reason { get; set; } + public required CatalogNotInstallableReason Reason { get; set; } } -/// A write may have completed. Recover and inspect durable state before retrying. -/// The recovery-required variant of . +/// The operation is not available on this runtime. Distinct from a network failure: nothing was attempted. +/// The unavailable variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationOutcomeRecoveryRequired : SkillInstallationOutcome +public partial class McpPlanInstallResultUnavailable : McpPlanInstallResult { /// [JsonIgnore] - public override string Kind => "recovery-required"; + public override string Kind => "unavailable"; - /// Operation whose durable result must be recovered and inspected. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } + + /// Why the operation is unavailable. + [JsonPropertyName("reason")] + public required CatalogUnavailableReason Reason { get; set; } } -/// The user declined the confirmation request. -/// The declined variant of . +/// The protocol version and capability set a caller requires, supplied on every catalog request so negotiation cannot be skipped by omission. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationOutcomeDeclined : SkillInstallationOutcome +public sealed class CatalogClientContract { - /// - [JsonIgnore] - public override string Kind => "declined"; + /// SDK protocol version the caller was generated against. A caller below the runtime's minimum supported version is refused rather than served a partial result. + [JsonPropertyName("protocolVersion")] + public long ProtocolVersion { get; set; } - /// Original operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// Wire features the caller requires the runtime to understand. Identifiers are bounded but extensible so a newer caller can negotiate with an older runtime. Requiring an unknown feature yields a typed refusal listing what is understood, never a partial grant. A grant does not promise that a deployment has enabled the operation; typed unavailable results report that separately. + [JsonPropertyName("requiredCapabilities")] + public IList RequiredCapabilities { get => field ??= []; set; } } -/// The operation was cancelled before a terminal mutation. -/// The cancelled variant of . +/// What an install plan is computed from: a candidate handle from a previous search, or a card supplied directly. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationOutcomeCancelled : SkillInstallationOutcome +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(McpPlanInstallSourceCandidate), "candidate")] +[JsonDerivedType(typeof(McpPlanInstallSourceCard), "card")] +public partial class McpPlanInstallSource { - /// - [JsonIgnore] - public override string Kind => "cancelled"; - - /// Original operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// The operation was refused without applying changes. -/// The refused variant of . + +/// Plan from a candidate returned by a previous catalog search. +/// The candidate variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationOutcomeRefused : SkillInstallationOutcome +public partial class McpPlanInstallSourceCandidate : McpPlanInstallSource { /// [JsonIgnore] - public override string Kind => "refused"; + public override string Kind => "candidate"; - /// Present once an operation has been allocated. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("operationId")] - public string? OperationId { get; set; } + /// Single-use candidate handle. Consumed by this call, so a replay of the same handle is rejected. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(256)] + [JsonPropertyName("candidateHandle")] + public required string CandidateHandle { get; set; } - /// Bounded refusal reason. - [JsonPropertyName("reason")] - public required SkillInstallationFailureReason Reason { get; set; } + /// The runtime- or authority-minted `searchId` returned with the search that produced this candidate. Planning verifies the private correlation and atomically consumes a matching candidate before downstream work, including attempts that subsequently fail or report unavailable. A mismatched search does not consume the candidate. It identifies a search rather than a person and must never be joined with user identity to re-identify anyone. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(64)] + [JsonPropertyName("searchId")] + public required string SearchId { get; set; } } -/// Original operation progress discriminator. -/// The completed variant of . +/// A card supplied directly by the caller. Exactly one of a URL or embedded data, encoded structurally so neither both nor neither can be expressed. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationOperationStatusCompleted : SkillInstallationOperationStatus +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(McpServerCardReferenceUrl), "url")] +[JsonDerivedType(typeof(McpServerCardReferenceEmbedded), "embedded")] +public partial class McpServerCardReference { - /// - [JsonIgnore] - public override string Phase => "completed"; - - /// Whether cancellation was requested before the terminal result. - [JsonPropertyName("cancellationRequested")] - public required bool CancellationRequested { get; set; } - - /// Original runtime-issued operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } - - /// Immutable terminal receipt. - [JsonPropertyName("outcome")] - public required SkillInstallationOutcome Outcome { get; set; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// Original-connection operation snapshot. -/// The operation variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationManagementOutcomeOperation : SkillInstallationManagementOutcome -{ - /// - [JsonIgnore] - public override string Kind => "operation"; - - /// Operation status. - [JsonPropertyName("operation")] - public required SkillInstallationOperationStatus Operation { get; set; } -} -/// Enablement changed and the selected session was reconciled. -/// The enabled-changed variant of . +/// An MCP server card to be retrieved from a URL through the runtime's hardened fetch boundary. +/// The url variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationManagementOutcomeEnabledChanged : SkillInstallationManagementOutcome +public partial class McpServerCardReferenceUrl : McpServerCardReference { /// [JsonIgnore] - public override string Kind => "enabled-changed"; + public override string Kind => "url"; - /// Safe reload or reconciliation diagnostics. - [JsonPropertyName("diagnostics")] - public required IList Diagnostics { get; set; } + /// Media type the card is expected to conform to. + [JsonPropertyName("mediaType")] + public required McpServerCardMediaType MediaType { get; set; } - /// Updated installation summary. - [JsonPropertyName("installation")] - public required SkillInstallationSummary Installation { get; set; } + /// Card URL. Retrieved only through the runtime's hardened boundary, with scheme, credential, address-range, redirect, timeout, and response-size controls applied. Never logged. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(2048)] + [JsonPropertyName("url")] + public required string Url { get; set; } } -/// The management request was refused. -/// The refused variant of . +/// An MCP server card supplied inline as an inert document. +/// The embedded variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationManagementOutcomeRefused : SkillInstallationManagementOutcome +public partial class McpServerCardReferenceEmbedded : McpServerCardReference { /// [JsonIgnore] - public override string Kind => "refused"; + public override string Kind => "embedded"; - /// Bounded refusal reason. - [JsonPropertyName("reason")] - public required SkillInstallationFailureReason Reason { get; set; } + /// The card document verbatim, treated as inert untrusted bytes. The runtime parses and validates it; the host is not expected to interpret it. Never logged. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(1048576)] + [JsonPropertyName("data")] + public required string Data { get; set; } + + /// Media type the card is expected to conform to. + [JsonPropertyName("mediaType")] + public required McpServerCardMediaType MediaType { get; set; } } -/// The outcome variant of . +/// Plan from a card supplied directly by the caller, without a preceding search. +/// The card variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationManagementResultOutcome : SkillInstallationManagementResult +public partial class McpPlanInstallSourceCard : McpPlanInstallSource { /// [JsonIgnore] - public override string Kind => "outcome"; - - /// Capabilities honoured for this request. - [JsonPropertyName("negotiated")] - public required CatalogNegotiatedContract Negotiated { get; set; } + public override string Kind => "card"; - /// Observed management outcome. - [JsonPropertyName("outcome")] - public required SkillInstallationManagementOutcome Outcome { get; set; } + /// The card to plan from: exactly one of a URL or embedded data. + [JsonPropertyName("card")] + public required McpServerCardReference Card { get; set; } } -/// The caller's protocol version or required capabilities cannot be honoured. Returned instead of a partial or ambiguous success. -/// The negotiation-refused variant of . +/// A side-effect-free request for an MCP install plan. Computing a plan never writes configuration, stores a secret, or reloads MCP servers. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationManagementResultNegotiationRefused : SkillInstallationManagementResult +public sealed class McpPlanInstallRequest { - /// - [JsonIgnore] - public override string Kind => "negotiation-refused"; + /// Protocol version and capabilities the caller requires. + [JsonPropertyName("contract")] + public required CatalogClientContract Contract { get; set; } - /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. + /// The same existing attached session that owns the original catalogue candidate. [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } - - /// Lowest caller protocol version this runtime will serve. - [JsonPropertyName("minimumSupportedProtocolVersion")] - public required long MinimumSupportedProtocolVersion { get; set; } - - /// Whether the version or the capability set was the problem. - [JsonPropertyName("reason")] - public required CatalogNegotiationRefusedReason Reason { get; set; } - - /// Protocol version of the runtime that refused the request. - [JsonPropertyName("runtimeProtocolVersion")] - public required long RuntimeProtocolVersion { get; set; } + [MinLength(1)] + [MaxLength(256)] + [JsonPropertyName("policySessionId")] + public string? PolicySessionId { get; set; } - /// Capabilities this runtime can safely advertise to this caller. The complete five-capability protocol-3 legacy set is always present; every capability added after that baseline appears only when the caller required it, so an older closed-enum decoder can still consume a refusal. This list does not imply that every deployment has enabled every operation. - [JsonPropertyName("supportedCapabilities")] - public required IList SupportedCapabilities { get; set; } + /// Configuration scope the plan targets. Defaults to user scope when omitted. + [JsonPropertyName("scope")] + public McpPlanScope? Scope { get; set; } - /// The subset of the caller's bounded extensible capability identifiers this runtime cannot honour. - [JsonPropertyName("unsupportedCapabilities")] - public required IList UnsupportedCapabilities { get; set; } + /// What to plan: either a candidate handle from a previous search, or a card supplied directly. + [JsonPropertyName("source")] + public required McpPlanInstallSource Source { get; set; } } -/// The request was rejected because a bounded field fell outside its permitted range or a required field was unusable. Pagination may also be rejected by the authority after a continuation request; repeat the search without page. -/// The invalid-request variant of . +/// Management result with contract receipt, or a typed request/negotiation refusal. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationManagementResultInvalidRequest : SkillInstallationManagementResult +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(McpInstallationManagementResultOutcome), "outcome")] +[JsonDerivedType(typeof(McpInstallationManagementResultNegotiationRefused), "negotiation-refused")] +[JsonDerivedType(typeof(McpInstallationManagementResultInvalidRequest), "invalid-request")] +public partial class McpInstallationManagementResult { - /// - [JsonIgnore] - public override string Kind => "invalid-request"; - - /// Which request field was rejected. - [JsonPropertyName("field")] - public required CatalogInvalidRequestField Field { get; set; } - - /// Human-readable explanation, safe to surface. Never echoes the offending value, nor a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// Side-effect-free planning of one verified Agent Finder Skill candidate. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SkillPlanInstallRequest -{ - /// Fresh single-use AI skill candidate handle returned by a bound catalogue search. - [JsonPropertyName("candidateHandle")] - public string CandidateHandle { get; set; } = string.Empty; - - /// Required authenticated bound catalogue and Skill installation capabilities. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); set; } - - /// Existing local session attached to this connection. - [JsonPropertyName("policySessionId")] - public string PolicySessionId { get; set; } = string.Empty; -} -/// Skill installation result with the honoured contract, or a typed request/negotiation refusal. +/// Read-only or recovery management result, never permission to activate or replay. /// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonPolymorphic( TypeDiscriminatorPropertyName = "kind", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(SkillInstallationResultOutcome), "outcome")] -[JsonDerivedType(typeof(SkillInstallationResultNegotiationRefused), "negotiation-refused")] -[JsonDerivedType(typeof(SkillInstallationResultInvalidRequest), "invalid-request")] -public partial class SkillInstallationResult +[JsonDerivedType(typeof(McpInstallationManagementOutcomeRecoveryRequired), "recovery-required")] +[JsonDerivedType(typeof(McpInstallationManagementOutcomeInstallPrepared), "install-prepared")] +[JsonDerivedType(typeof(McpInstallationManagementOutcomeListed), "listed")] +[JsonDerivedType(typeof(McpInstallationManagementOutcomeRecovered), "recovered")] +[JsonDerivedType(typeof(McpInstallationManagementOutcomeUninstallPlanned), "uninstall-planned")] +[JsonDerivedType(typeof(McpInstallationManagementOutcomeOperation), "operation")] +[JsonDerivedType(typeof(McpInstallationManagementOutcomeRefused), "refused")] +public partial class McpInstallationManagementOutcome { /// The type discriminator. [JsonPropertyName("kind")] @@ -5028,391 +5407,498 @@ public partial class SkillInstallationResult } -/// The outcome variant of . +/// Already-confirmed durable work must be reconciled before new mutations or inventory. +/// The recovery-required variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationResultOutcome : SkillInstallationResult +public partial class McpInstallationManagementOutcomeRecoveryRequired : McpInstallationManagementOutcome { /// [JsonIgnore] - public override string Kind => "outcome"; + public override string Kind => "recovery-required"; +} - /// Capabilities honoured for this request. - [JsonPropertyName("negotiated")] - public required CatalogNegotiatedContract Negotiated { get; set; } +/// Inert, runtime-owned admission. The operation ID is known before confirmation or effects. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpPreparedInstall +{ + /// Original plan expiry in Unix epoch milliseconds; preparation does not extend it. + [JsonPropertyName("expiresAtEpochMs")] + public long ExpiresAtEpochMs { get; set; } - /// Terminal operation outcome. - [JsonPropertyName("outcome")] - public required SkillInstallationOutcome Outcome { get; set; } + /// Original connection-owned operation, known before the first confirmation callback. + [JsonPropertyName("operationId")] + public string OperationId { get; set; } = string.Empty; } -/// The caller's protocol version or required capabilities cannot be honoured. Returned instead of a partial or ambiguous success. -/// The negotiation-refused variant of . +/// The install-prepared variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationResultNegotiationRefused : SkillInstallationResult +public partial class McpInstallationManagementOutcomeInstallPrepared : McpInstallationManagementOutcome { /// [JsonIgnore] - public override string Kind => "negotiation-refused"; - - /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + public override string Kind => "install-prepared"; - /// Lowest caller protocol version this runtime will serve. - [JsonPropertyName("minimumSupportedProtocolVersion")] - public required long MinimumSupportedProtocolVersion { get; set; } + /// Known original operation identity, returned before callback or effects. + [JsonPropertyName("operation")] + public required McpPreparedInstall Operation { get; set; } +} - /// Whether the version or the capability set was the problem. - [JsonPropertyName("reason")] - public required CatalogNegotiationRefusedReason Reason { get; set; } +/// A versioned, bounded trust observation carried unchanged with a catalog candidate and its private handle context. Current observations require a recognised T1/T2 tier; every non-current state structurally forbids a tier. Eligibility remains `unknown` while Agent Finder supplies no exposure decision, and states absent from its current wire are never inferred from age, relevance, popularity, or a tier transition. +/// Polymorphic base type discriminated by status. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "status", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(CatalogTrustSnapshotCurrent), "current")] +[JsonDerivedType(typeof(CatalogTrustSnapshotAbsent), "absent")] +[JsonDerivedType(typeof(CatalogTrustSnapshotStale), "stale")] +[JsonDerivedType(typeof(CatalogTrustSnapshotDowngraded), "downgraded")] +[JsonDerivedType(typeof(CatalogTrustSnapshotRevoked), "revoked")] +[JsonDerivedType(typeof(CatalogTrustSnapshotUnsupported), "unsupported")] +[JsonDerivedType(typeof(CatalogTrustSnapshotMalformed), "malformed")] +public partial class CatalogTrustSnapshot +{ + /// The type discriminator. + [JsonPropertyName("status")] + public virtual string Status { get; set; } = string.Empty; +} - /// Protocol version of the runtime that refused the request. - [JsonPropertyName("runtimeProtocolVersion")] - public required long RuntimeProtocolVersion { get; set; } - /// Capabilities this runtime can safely advertise to this caller. The complete five-capability protocol-3 legacy set is always present; every capability added after that baseline appears only when the caller required it, so an older closed-enum decoder can still consume a refusal. This list does not imply that every deployment has enabled every operation. - [JsonPropertyName("supportedCapabilities")] - public required IList SupportedCapabilities { get; set; } +/// Where and when the runtime observed the trust metadata. Observation time is not the authority's evaluation time and must not be used to infer staleness. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class CatalogTrustProvenance +{ + /// ISO 8601 timestamp with a timezone offset at which the runtime observed the search result carrying this trust field. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(20)] + [MaxLength(64)] + [JsonPropertyName("observedAt")] + public DateTimeOffset ObservedAt { get; set; } - /// The subset of the caller's bounded extensible capability identifiers this runtime cannot honour. - [JsonPropertyName("unsupportedCapabilities")] - public required IList UnsupportedCapabilities { get; set; } + /// Bounded authority that supplied the trust field. + [JsonPropertyName("source")] + public CatalogTrustSource Source { get; set; } } -/// The request was rejected because a bounded field fell outside its permitted range or a required field was unusable. Pagination may also be rejected by the authority after a continuation request; repeat the search without page. -/// The invalid-request variant of . +/// A recognised current Agent Finder T1 or T2 trust tier. +/// The current variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationResultInvalidRequest : SkillInstallationResult +public partial class CatalogTrustSnapshotCurrent : CatalogTrustSnapshot { /// [JsonIgnore] - public override string Kind => "invalid-request"; + public override string Status => "current"; - /// Which request field was rejected. - [JsonPropertyName("field")] - public required CatalogInvalidRequestField Field { get; set; } + /// Service-computed exposure eligibility. `unknown` is required while Agent Finder returns no explicit eligibility field. + [JsonPropertyName("eligibility")] + public required CatalogTrustEligibility Eligibility { get; set; } - /// Human-readable explanation, safe to surface. Never echoes the offending value, nor a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Bounded source and observation time for this snapshot. This is distinct from evidence used by the authority to calculate trust. + [JsonPropertyName("provenance")] + public required CatalogTrustProvenance Provenance { get; set; } + + /// Schema version of this runtime-owned snapshot envelope. + [JsonPropertyName("schemaVersion")] + public required CatalogTrustSnapshotSchemaVersion SchemaVersion { get; set; } + + /// Service-computed T1 or T2 trust tier. + [JsonPropertyName("tier")] + public required CatalogTrustTier Tier { get; set; } } -/// Applies exactly one retained verified Skill installation plan. +/// Discriminator: the authority omitted trust metadata. +/// The absent variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SkillApplyInstallRequest +public partial class CatalogTrustSnapshotAbsent : CatalogTrustSnapshot { - /// Required authenticated bound catalogue and Skill installation capabilities. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); set; } + /// + [JsonIgnore] + public override string Status => "absent"; - /// Opaque original plan, consumed once. - [JsonPropertyName("planHandle")] - public string PlanHandle { get; set; } = string.Empty; + /// Service-computed exposure eligibility. `unknown` is required while Agent Finder returns no explicit eligibility field. + [JsonPropertyName("eligibility")] + public required CatalogTrustEligibility Eligibility { get; set; } - /// Same existing selected session as planning. - [JsonPropertyName("policySessionId")] - public string PolicySessionId { get; set; } = string.Empty; + /// Bounded source and observation time for this snapshot. This is distinct from evidence used by the authority to calculate trust. + [JsonPropertyName("provenance")] + public required CatalogTrustProvenance Provenance { get; set; } + + /// Schema version of this runtime-owned snapshot envelope. + [JsonPropertyName("schemaVersion")] + public required CatalogTrustSnapshotSchemaVersion SchemaVersion { get; set; } } -/// Read-only preparation of one owned Skill removal under fresh selected-session authority. +/// Discriminator: the authority explicitly marked the assessment stale. +/// The stale variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SkillPlanUninstallRequest +public partial class CatalogTrustSnapshotStale : CatalogTrustSnapshot { - /// Required authenticated bound installation contract. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); set; } + /// + [JsonIgnore] + public override string Status => "stale"; - /// Exact receipt to inspect. - [JsonPropertyName("installationId")] - public string InstallationId { get; set; } = string.Empty; + /// Service-computed exposure eligibility. `unknown` is required while Agent Finder returns no explicit eligibility field. + [JsonPropertyName("eligibility")] + public required CatalogTrustEligibility Eligibility { get; set; } - /// Existing selected local session on this connection. - [JsonPropertyName("policySessionId")] - public string PolicySessionId { get; set; } = string.Empty; + /// Bounded source and observation time for this snapshot. This is distinct from evidence used by the authority to calculate trust. + [JsonPropertyName("provenance")] + public required CatalogTrustProvenance Provenance { get; set; } + + /// Schema version of this runtime-owned snapshot envelope. + [JsonPropertyName("schemaVersion")] + public required CatalogTrustSnapshotSchemaVersion SchemaVersion { get; set; } } -/// One-use application of the exact retained Skill removal plan. +/// Discriminator: the authority explicitly reported a downgraded assessment. +/// The downgraded variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SkillApplyUninstallRequest +public partial class CatalogTrustSnapshotDowngraded : CatalogTrustSnapshot { - /// Required authenticated bound installation contract. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); set; } + /// + [JsonIgnore] + public override string Status => "downgraded"; - /// Opaque original removal plan, consumed once. - [JsonPropertyName("planHandle")] - public string PlanHandle { get; set; } = string.Empty; + /// Service-computed exposure eligibility. `unknown` is required while Agent Finder returns no explicit eligibility field. + [JsonPropertyName("eligibility")] + public required CatalogTrustEligibility Eligibility { get; set; } - /// Same existing selected session as removal preparation. - [JsonPropertyName("policySessionId")] - public string PolicySessionId { get; set; } = string.Empty; + /// Bounded source and observation time for this snapshot. This is distinct from evidence used by the authority to calculate trust. + [JsonPropertyName("provenance")] + public required CatalogTrustProvenance Provenance { get; set; } + + /// Schema version of this runtime-owned snapshot envelope. + [JsonPropertyName("schemaVersion")] + public required CatalogTrustSnapshotSchemaVersion SchemaVersion { get; set; } } -/// Server-side skill metadata, including name, description, source, enabled/invocable state, path, project path, and argument hint. +/// Discriminator: the authority explicitly revoked the assessment. +/// The revoked variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ServerSkill +public partial class CatalogTrustSnapshotRevoked : CatalogTrustSnapshot { - /// Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field. - [JsonPropertyName("argumentHint")] - public string? ArgumentHint { get; set; } - - /// Canonical slash command name used to invoke the skill, without the leading '/'. - [JsonPropertyName("commandName")] - public string? CommandName { get; set; } + /// + [JsonIgnore] + public override string Status => "revoked"; - /// Description of what the skill does. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; + /// Service-computed exposure eligibility. `unknown` is required while Agent Finder returns no explicit eligibility field. + [JsonPropertyName("eligibility")] + public required CatalogTrustEligibility Eligibility { get; set; } - /// Whether the skill is currently enabled (based on global config). - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } + /// Bounded source and observation time for this snapshot. This is distinct from evidence used by the authority to calculate trust. + [JsonPropertyName("provenance")] + public required CatalogTrustProvenance Provenance { get; set; } - /// Unique identifier for the skill. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Schema version of this runtime-owned snapshot envelope. + [JsonPropertyName("schemaVersion")] + public required CatalogTrustSnapshotSchemaVersion SchemaVersion { get; set; } +} - /// Absolute path to the skill file. - [JsonPropertyName("path")] - public string? Path { get; set; } +/// Discriminator: the authority supplied a bounded trust value this runtime does not understand. +/// The unsupported variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class CatalogTrustSnapshotUnsupported : CatalogTrustSnapshot +{ + /// + [JsonIgnore] + public override string Status => "unsupported"; - /// The project path this skill belongs to (only for project/inherited skills). - [JsonPropertyName("projectPath")] - public string? ProjectPath { get; set; } + /// Service-computed exposure eligibility. `unknown` is required while Agent Finder returns no explicit eligibility field. + [JsonPropertyName("eligibility")] + public required CatalogTrustEligibility Eligibility { get; set; } - /// Source location type (e.g., project, personal-copilot, plugin, builtin). - [JsonPropertyName("source")] - public SkillSource Source { get; set; } + /// Bounded source and observation time for this snapshot. This is distinct from evidence used by the authority to calculate trust. + [JsonPropertyName("provenance")] + public required CatalogTrustProvenance Provenance { get; set; } - /// Whether the skill can be invoked by the user as a slash command. - [JsonPropertyName("userInvocable")] - public bool UserInvocable { get; set; } + /// Schema version of this runtime-owned snapshot envelope. + [JsonPropertyName("schemaVersion")] + public required CatalogTrustSnapshotSchemaVersion SchemaVersion { get; set; } } -/// Skills discovered across global and project sources. +/// Discriminator: the trust field was empty, unbounded, or had the wrong JSON type. +/// The malformed variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ServerSkillList +public partial class CatalogTrustSnapshotMalformed : CatalogTrustSnapshot { - /// Messages for skills that failed to load (e.g. malformed SKILL.md). Empty when host skills are excluded so host-local paths are not disclosed to multitenant callers. - [JsonPropertyName("errors")] - public IList? Errors { get; set; } + /// + [JsonIgnore] + public override string Status => "malformed"; - /// All discovered skills across all sources. - [JsonPropertyName("skills")] - public IList Skills { get => field ??= []; set; } + /// Service-computed exposure eligibility. `unknown` is required while Agent Finder returns no explicit eligibility field. + [JsonPropertyName("eligibility")] + public required CatalogTrustEligibility Eligibility { get; set; } + + /// Bounded source and observation time for this snapshot. This is distinct from evidence used by the authority to calculate trust. + [JsonPropertyName("provenance")] + public required CatalogTrustProvenance Provenance { get; set; } + + /// Schema version of this runtime-owned snapshot envelope. + [JsonPropertyName("schemaVersion")] + public required CatalogTrustSnapshotSchemaVersion SchemaVersion { get; set; } } -/// Optional project paths and additional skill directories to include in discovery. +/// Catalogue identity retained from a bound candidate or plan at installation time. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SkillsDiscoverRequest +public sealed class InstallationCatalogueIdentity { - /// When true, omit skills from the host's global sources (personal, custom, plugin, and built-in), returning only project-scoped skills. For multitenant deployments. - [JsonPropertyName("excludeHostSkills")] - public bool? ExcludeHostSkills { get; set; } + /// Catalogue description retained at install planning time. + [JsonPropertyName("description")] + public string? Description { get; set; } - /// Optional skill scan paths to exclude from discovery. - [JsonPropertyName("ignoredSkillsLocations")] - public IList? IgnoredSkillsLocations { get; set; } + /// Human display name retained from the catalogue candidate. + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } = string.Empty; - /// Optional list of project directory paths to scan for project-scoped skills. - [JsonPropertyName("projectPaths")] - public IList? ProjectPaths { get; set; } + /// Catalogue item URL when supplied by the authority. + [JsonPropertyName("itemUrl")] + public string? ItemUrl { get; set; } - /// Optional list of additional skill directory paths to include. - [JsonPropertyName("skillDirectories")] - public IList? SkillDirectories { get; set; } -} + /// Catalogue publisher retained at install planning time. + [JsonPropertyName("publisher")] + public string? Publisher { get; set; } -/// Canonical directory where skills can be discovered or created, with scope, preference, and optional project path. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillDiscoveryPath -{ - /// Absolute path of the create/discovery target (may not exist on disk yet). - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Authority resource identifier when supplied by the catalogue. + [JsonPropertyName("resourceId")] + public string? ResourceId { get; set; } - /// Whether this is the canonical directory to create a new skill in its tier. At most one entry per tier is preferred; the `personal-agents` and `custom` scopes are never preferred. - [JsonPropertyName("preferredForCreation")] - public bool PreferredForCreation { get; set; } + /// Catalogue authority/source string that supplied the candidate. + [JsonPropertyName("source")] + public string Source { get; set; } = string.Empty; - /// The input project path this directory was derived from (only for project scope). - [JsonPropertyName("projectPath")] - public string? ProjectPath { get; set; } + /// Catalogue trust observation retained at install planning time. + [JsonPropertyName("trustAtInstall")] + public CatalogTrustSnapshot? TrustAtInstall { get; set; } - /// Which tier this directory belongs to. - [JsonPropertyName("scope")] - public SkillDiscoveryScope Scope { get; set; } + /// Catalogue version retained at install planning time. + [JsonPropertyName("version")] + public string? Version { get; set; } } -/// Canonical locations where skills can be created so the runtime will recognize them. +/// Durable configuration ownership is distinct from session-specific usability. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillDiscoveryPathList +public sealed class McpInstallationSummary { - /// Canonical skill create/discovery directories, in priority order. - [JsonPropertyName("paths")] - public IList Paths { get => field ??= []; set; } + /// Catalogue identity retained from the installed plan when available. + [JsonPropertyName("catalogue")] + public InstallationCatalogueIdentity? Catalogue { get; set; } + + /// Exact alternative retained in the installing receipt. + [JsonPropertyName("choiceId")] + public string ChoiceId { get; set; } = string.Empty; + + /// Identity retained from the validated original plan. + [JsonPropertyName("identity")] + public McpPlanResourceIdentity Identity { get => field ??= new(); set; } + + /// Exact durable installation receipt identity. + [JsonPropertyName("installationId")] + public string InstallationId { get; set; } = string.Empty; + + /// ISO 8601 wall-clock installation time when available. + [JsonPropertyName("installedAt")] + public string? InstalledAt { get; set; } + + /// Original installing operation, not a fresh management operation. + [JsonPropertyName("operationId")] + public string OperationId { get; set; } = string.Empty; + + /// Ownership or setup state, never inferred proof of tool usability. + [JsonPropertyName("state")] + public McpInstallationState State { get; set; } } -/// Optional project paths to enumerate. +/// The listed variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SkillsGetDiscoveryPathsRequest +public partial class McpInstallationManagementOutcomeListed : McpInstallationManagementOutcome { - /// When true, omit the host's personal and custom skill directories, leaving only project directories. For multitenant deployments. - [JsonPropertyName("excludeHostSkills")] - public bool? ExcludeHostSkills { get; set; } - - /// Optional skill scan paths to exclude from discovery. - [JsonPropertyName("ignoredSkillsLocations")] - public IList? IgnoredSkillsLocations { get; set; } + /// + [JsonIgnore] + public override string Kind => "listed"; - /// Optional list of project directory paths. When omitted or empty, only personal and custom directories are returned. - [JsonPropertyName("projectPaths")] - public IList? ProjectPaths { get; set; } + /// Owned receipts visible to the selected account and host. + [JsonPropertyName("installations")] + public required IList Installations { get; set; } } -/// Inventory request under an explicitly selected existing session. +/// The recovered variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SkillInstallationsRequest +public partial class McpInstallationManagementOutcomeRecovered : McpInstallationManagementOutcome { - /// Required authenticated bound installation contract. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); set; } + /// + [JsonIgnore] + public override string Kind => "recovered"; - /// Existing selected local session on this connection. - [JsonPropertyName("policySessionId")] - public string PolicySessionId { get; set; } = string.Empty; + /// Freshly inspected receipts after successful durable reconciliation. + [JsonPropertyName("installations")] + public required IList Installations { get; set; } } -/// Existing-operation control. A new session selector is deliberately not accepted. +/// Exact inert removal plan. No configuration or credentials have changed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SkillInstallationOperationRequest +public sealed class McpUninstallPlan { - /// Required authenticated bound Skill installation capability. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); set; } + /// Original wall-clock expiry in milliseconds. Applying never renews it. + [JsonPropertyName("expiresAtEpochMs")] + public long ExpiresAtEpochMs { get; set; } - /// Exact runtime-issued operation ID on the original connection. + /// Original owned receipt being removed. + [JsonPropertyName("installation")] + public McpInstallationSummary Installation { get => field ??= new(); set; } + + /// The original operation, inspectable and cancellable on this same connection. [JsonPropertyName("operationId")] public string OperationId { get; set; } = string.Empty; + + /// Exact configured input slots owned by this installation, never shared OAuth tokens. + [JsonPropertyName("ownedSecretCount")] + public long OwnedSecretCount { get; set; } + + /// One-use original connection and authority-bound plan handle. + [JsonPropertyName("planHandle")] + public string PlanHandle { get; set; } = string.Empty; + + /// Shared authentication is deliberately retained; revocation is a separate action. + [JsonPropertyName("preservesSharedAuthentication")] + public bool PreservesSharedAuthentication { get; set; } + + /// Whether removal restores a protected earlier configuration. + [JsonPropertyName("restoresPreviousConfiguration")] + public bool RestoresPreviousConfiguration { get; set; } } -/// Persisted enablement update for one owned Skill installation. +/// The uninstall-planned variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SkillSetEnabledRequest +public partial class McpInstallationManagementOutcomeUninstallPlanned : McpInstallationManagementOutcome { - /// Required authenticated bound Skill installation capability. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); set; } - - /// Persisted enablement value. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } - - /// Exact receipt identity to update. - [JsonPropertyName("installationId")] - public string InstallationId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "uninstall-planned"; - /// Existing selected local session to reconcile after persistence. - [JsonPropertyName("policySessionId")] - public string PolicySessionId { get; set; } = string.Empty; + /// Original owned removal plan and operation. + [JsonPropertyName("plan")] + public required McpUninstallPlan Plan { get; set; } } -/// Skill names to mark as disabled in global configuration, replacing any previous list. +/// Status snapshot from the original connection, independent of new-work account availability. +/// Polymorphic base type discriminated by phase. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SkillsConfigSetDisabledSkillsRequest +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "phase", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(McpInstallationOperationStatusPreparing), "preparing")] +[JsonDerivedType(typeof(McpInstallationOperationStatusPrepared), "prepared")] +[JsonDerivedType(typeof(McpInstallationOperationStatusAwaitingConfirmation), "awaiting-confirmation")] +[JsonDerivedType(typeof(McpInstallationOperationStatusRevalidating), "revalidating")] +[JsonDerivedType(typeof(McpInstallationOperationStatusApplying), "applying")] +[JsonDerivedType(typeof(McpInstallationOperationStatusCompleted), "completed")] +public partial class McpInstallationOperationStatus { - /// List of skill names to disable. - [JsonPropertyName("disabledSkills")] - public IList DisabledSkills { get => field ??= []; set; } + /// The type discriminator. + [JsonPropertyName("phase")] + public virtual string Phase { get; set; } = string.Empty; } -/// Adds or removes a single skill from the global disabled list, leaving every other entry untouched. + +/// The preparing variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SkillsConfigSetSkillDisabledRequest +public partial class McpInstallationOperationStatusPreparing : McpInstallationOperationStatus { - /// True to disable the skill, false to enable it. - [JsonPropertyName("disabled")] - public bool Disabled { get; set; } + /// + [JsonIgnore] + public override string Phase => "preparing"; - /// Name of the skill to add to or remove from the disabled list. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Whether cancellation has been requested, not proof that a write was undone. + [JsonPropertyName("cancellationRequested")] + public required bool CancellationRequested { get; set; } + + /// Original runtime-issued operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } -/// Outcome of a catalog.search call: either bounded inert candidates, or one typed refusal. Never a partial success. -/// Polymorphic base type discriminated by kind. +/// The prepared variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(CatalogSearchResultSucceeded), "succeeded")] -[JsonDerivedType(typeof(CatalogSearchResultNegotiationRefused), "negotiation-refused")] -[JsonDerivedType(typeof(CatalogSearchResultUnsupportedKind), "unsupported-kind")] -[JsonDerivedType(typeof(CatalogSearchResultInvalidRequest), "invalid-request")] -[JsonDerivedType(typeof(CatalogSearchResultAuthenticationRequired), "authentication-required")] -[JsonDerivedType(typeof(CatalogSearchResultPolicyRejected), "policy-rejected")] -[JsonDerivedType(typeof(CatalogSearchResultNetworkFailure), "network-failure")] -[JsonDerivedType(typeof(CatalogSearchResultUnsafeRetrieval), "unsafe-retrieval")] -[JsonDerivedType(typeof(CatalogSearchResultMalformedCard), "malformed-card")] -[JsonDerivedType(typeof(CatalogSearchResultContractViolation), "contract-violation")] -[JsonDerivedType(typeof(CatalogSearchResultUnavailable), "unavailable")] -public partial class CatalogSearchResult +public partial class McpInstallationOperationStatusPrepared : McpInstallationOperationStatus { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// + [JsonIgnore] + public override string Phase => "prepared"; + /// Whether the inert prepared operation was asked to cancel. + [JsonPropertyName("cancellationRequested")] + public required bool CancellationRequested { get; set; } -/// One inert catalog result, represented as an MCP server, discovery-only AI skill, or opt-in Agent Plugin variant so kind, media type, provenance, and available operations cannot contradict each other. -/// Polymorphic base type discriminated by kind. + /// Original runtime-issued operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } +} + +/// The awaiting-confirmation variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(CatalogCandidateMcpServer), "mcp-server")] -[JsonDerivedType(typeof(CatalogCandidateAiSkill), "ai-skill")] -[JsonDerivedType(typeof(CatalogCandidatePlugin), "plugin")] -public partial class CatalogCandidate +public partial class McpInstallationOperationStatusAwaitingConfirmation : McpInstallationOperationStatus { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Phase => "awaiting-confirmation"; + + /// Whether the pending human callback was asked to cancel. + [JsonPropertyName("cancellationRequested")] + public required bool CancellationRequested { get; set; } + + /// Original runtime-issued operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } +/// The revalidating variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class McpInstallationOperationStatusRevalidating : McpInstallationOperationStatus +{ + /// + [JsonIgnore] + public override string Phase => "revalidating"; -/// Where and when an MCP server catalog reference was observed. Discovery provenance deliberately carries no content digest because search does not establish the exact validated content a later plan will bind. + /// Whether source or authority revalidation was asked to cancel. + [JsonPropertyName("cancellationRequested")] + public required bool CancellationRequested { get; set; } + + /// Original runtime-issued operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } +} + +/// The applying variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CatalogMcpServerCandidateProvenance +public partial class McpInstallationOperationStatusApplying : McpInstallationOperationStatus { - /// Host of the catalog authority that advertised the reference, without path, query, or credentials. Inert untrusted data. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("authority")] - public string Authority { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Phase => "applying"; - /// JSON MCP media type advertised for the referenced card. - [JsonPropertyName("mediaType")] - public McpServerCardMediaType MediaType { get; set; } + /// Whether applying was asked to cancel; already-started effects retain their lease. + [JsonPropertyName("cancellationRequested")] + public required bool CancellationRequested { get; set; } - /// ISO 8601 timestamp at which the runtime observed the catalog reference. This is not a retrieval or validation timestamp. - [JsonPropertyName("observedAt")] - public string ObservedAt { get; set; } = string.Empty; + /// Original runtime-issued operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } -/// Where a candidate's card came from. Exactly one of a URL or embedded data: the union has no variant carrying both, and no variant carrying neither, so the rule holds structurally rather than by validation. +/// Terminal mutation result. Uncertainty is not approval, rollback or permission to replay. /// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonPolymorphic( TypeDiscriminatorPropertyName = "kind", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(CatalogCandidateSourceUrl), "url")] -[JsonDerivedType(typeof(CatalogCandidateSourceEmbedded), "embedded")] -public partial class CatalogCandidateSource +[JsonDerivedType(typeof(McpInstallationOutcomeInstalled), "installed")] +[JsonDerivedType(typeof(McpInstallationOutcomeUninstalled), "uninstalled")] +[JsonDerivedType(typeof(McpInstallationOutcomeRolledBack), "rolled-back")] +[JsonDerivedType(typeof(McpInstallationOutcomeRecoveryRequired), "recovery-required")] +[JsonDerivedType(typeof(McpInstallationOutcomeDeclined), "declined")] +[JsonDerivedType(typeof(McpInstallationOutcomeCancelled), "cancelled")] +[JsonDerivedType(typeof(McpInstallationOutcomeRefused), "refused")] +public partial class McpInstallationOutcome { /// The type discriminator. [JsonPropertyName("kind")] @@ -5420,350 +5906,200 @@ public partial class CatalogCandidateSource } -/// Candidate whose card is retrieved from a URL through the runtime's hardened fetch boundary. -/// The url variant of . +/// The installed variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogCandidateSourceUrl : CatalogCandidateSource +public partial class McpInstallationOutcomeInstalled : McpInstallationOutcome { /// [JsonIgnore] - public override string Kind => "url"; + public override string Kind => "installed"; - /// Card URL as advertised. Inert untrusted data: the runtime retrieves it only through its own hardened boundary, and it is never logged. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("url")] - public required string Url { get; set; } + /// Durable installation succeeded but final transaction cleanup remains. + [JsonPropertyName("cleanupPending")] + public required bool CleanupPending { get; set; } + + /// Receipt identity produced by the confirmed transaction. + [JsonPropertyName("installation")] + public required McpInstallationSummary Installation { get; set; } } -/// Candidate whose card reference arrived inline. The document and its content-derived properties stay behind the runtime boundary. -/// The embedded variant of . +/// The uninstalled variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogCandidateSourceEmbedded : CatalogCandidateSource +public partial class McpInstallationOutcomeUninstalled : McpInstallationOutcome { /// [JsonIgnore] - public override string Kind => "embedded"; + public override string Kind => "uninstalled"; + + /// Durable removal succeeded but final cleanup remains. + [JsonPropertyName("cleanupPending")] + public required bool CleanupPending { get; set; } + + /// Exact removed receipt identity. + [JsonPropertyName("installationId")] + public required string InstallationId { get; set; } + + /// Original removal operation. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } + + /// Any grants in the incumbent shared OAuth store remain unowned and retained. + [JsonPropertyName("preservedSharedAuthentication")] + public required bool PreservedSharedAuthentication { get; set; } + + /// Exact owned input slots removed, excluding shared OAuth credentials. + [JsonPropertyName("removedOwnedSecrets")] + public required long RemovedOwnedSecrets { get; set; } + + /// Whether protected pre-install configuration was restored. + [JsonPropertyName("restoredPreviousConfiguration")] + public required bool RestoredPreviousConfiguration { get; set; } } -/// An inert MCP server catalog result. Every free-text field is untrusted external data and must never be treated as an instruction, and the handle is the only way to refer to the candidate in a later operation. -/// The mcp-server variant of . +/// The durable transaction was aborted or fully compensated. +/// The rolled-back variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogCandidateMcpServer : CatalogCandidate +public partial class McpInstallationOutcomeRolledBack : McpInstallationOutcome { /// [JsonIgnore] - public override string Kind => "mcp-server"; - - /// Description taken verbatim from the card. Inert untrusted text. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("description")] - public string? Description { get; set; } - - /// Display name taken verbatim from the card. Inert untrusted text. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(200)] - [JsonPropertyName("displayName")] - public required string DisplayName { get; set; } + public override string Kind => "rolled-back"; - /// Opaque, runtime-instance scoped, TTL-bound, single-use handle for this candidate. Carries no readable information and is rejected when stale, replayed, or presented to a different runtime instance. Never logged. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("handle")] - public required string Handle { get; set; } + /// Original connection-owned operation. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } - /// ISO 8601 timestamp after which the handle is stale and will be rejected. - [JsonPropertyName("handleExpiresAt")] - public required string HandleExpiresAt { get; set; } + /// Cause of the fully aborted or compensated operation. + [JsonPropertyName("reason")] + public required McpInstallationFailureReason Reason { get; set; } +} - /// Whether this MCP server can be planned for installation, and if policy prevents it. - [JsonPropertyName("installability")] - public required CatalogMcpServerInstallability Installability { get; set; } +/// A write may have completed. Recover and inspect durable state before retrying. +/// The recovery-required variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class McpInstallationOutcomeRecoveryRequired : McpInstallationOutcome +{ + /// + [JsonIgnore] + public override string Kind => "recovery-required"; - /// JSON MCP media type of the underlying card. - [JsonPropertyName("mediaType")] - public required McpServerCardMediaType MediaType { get; set; } + /// Operation whose durable result must be recovered and inspected. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } +} - /// Where the catalog reference was observed, without the card itself or any content digest. - [JsonPropertyName("provenance")] - public required CatalogMcpServerCandidateProvenance Provenance { get; set; } +/// The declined variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class McpInstallationOutcomeDeclined : McpInstallationOutcome +{ + /// + [JsonIgnore] + public override string Kind => "declined"; - /// Publisher taken verbatim from the card. Inert untrusted text. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(200)] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("publisher")] - public string? Publisher { get; set; } + /// Original operation explicitly declined by the user. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } +} - /// Where the card came from: exactly one of a URL or embedded data, encoded as a tagged union so neither both nor neither can be represented. - [JsonPropertyName("source")] - public required CatalogCandidateSource Source { get; set; } +/// The cancelled variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class McpInstallationOutcomeCancelled : McpInstallationOutcome +{ + /// + [JsonIgnore] + public override string Kind => "cancelled"; - /// Versioned trust metadata observed from the catalog authority. Optional for protocol-3 compatibility with runtimes that predate trust snapshots. A trust-capable runtime emits an explicit snapshot even when the authority omitted or malformed its trust field. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("trust")] - public CatalogTrustSnapshot? Trust { get; set; } + /// Original operation cancelled before a terminal application result. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } -/// Where and when an AI skill catalog reference was observed. Discovery provenance deliberately carries no content digest because search does not establish the exact validated content a later plan will bind. +/// The refused variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CatalogAiSkillCandidateProvenance +public partial class McpInstallationOutcomeRefused : McpInstallationOutcome { - /// Host of the catalog authority that advertised the reference, without path, query, or credentials. Inert untrusted data. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("authority")] - public string Authority { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "refused"; - /// Media type advertised for the referenced AI skill card. - [JsonPropertyName("mediaType")] - public CatalogAiSkillMediaType MediaType { get; set; } + /// Present once an operation has been allocated; never a plan handle. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("operationId")] + public string? OperationId { get; set; } - /// ISO 8601 timestamp at which the runtime observed the catalog reference. This is not a retrieval or validation timestamp. - [JsonPropertyName("observedAt")] - public string ObservedAt { get; set; } = string.Empty; + /// Specific bounded refusal, never a success-shaped fallback. + [JsonPropertyName("reason")] + public required McpInstallationFailureReason Reason { get; set; } } -/// An inert AI skill catalog result. Verified Skill candidates may be installable only when the runtime reports installability and the selected session is permitted to plan installation. -/// The ai-skill variant of . +/// The completed variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogCandidateAiSkill : CatalogCandidate +public partial class McpInstallationOperationStatusCompleted : McpInstallationOperationStatus { /// [JsonIgnore] - public override string Kind => "ai-skill"; + public override string Phase => "completed"; - /// Description taken verbatim from the card. Inert untrusted text. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("description")] - public string? Description { get; set; } + /// Whether cancellation was requested before the terminal result. + [JsonPropertyName("cancellationRequested")] + public required bool CancellationRequested { get; set; } - /// Display name taken verbatim from the card. Inert untrusted text. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(200)] - [JsonPropertyName("displayName")] - public required string DisplayName { get; set; } - - /// Opaque, runtime-instance scoped, TTL-bound, single-use handle for this candidate. Carries no readable information and is rejected when stale, replayed, or presented to a different runtime instance. Never logged. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("handle")] - public required string Handle { get; set; } - - /// ISO 8601 timestamp after which the handle is stale and will be rejected. - [JsonPropertyName("handleExpiresAt")] - public required string HandleExpiresAt { get; set; } - - /// Whether this AI skill candidate can be planned for verified installation in the selected session. - [JsonPropertyName("installability")] - public required CatalogAiSkillInstallability Installability { get; set; } - - /// Media type of the underlying AI skill card. - [JsonPropertyName("mediaType")] - public required CatalogAiSkillMediaType MediaType { get; set; } - - /// Where the catalog reference was observed, without the card itself or any content digest. - [JsonPropertyName("provenance")] - public required CatalogAiSkillCandidateProvenance Provenance { get; set; } - - /// Publisher taken verbatim from the card. Inert untrusted text. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(200)] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("publisher")] - public string? Publisher { get; set; } - - /// Where the card came from: exactly one of a URL or embedded data, encoded as a tagged union so neither both nor neither can be represented. - [JsonPropertyName("source")] - public required CatalogCandidateSource Source { get; set; } - - /// Versioned trust metadata observed from the catalog authority. Optional for protocol-3 compatibility with runtimes that predate trust snapshots. A trust-capable runtime emits an explicit snapshot even when the authority omitted or malformed its trust field. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("trust")] - public CatalogTrustSnapshot? Trust { get; set; } -} - -/// Where and when an Agent Plugin catalog reference was observed. Discovery provenance deliberately carries no descriptor URL, raw data, candidate handle, or content digest. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CatalogAgentPluginCandidateProvenance -{ - /// Host of the catalog authority that advertised the reference, without path, query, or credentials. Inert untrusted data. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("authority")] - public string Authority { get; set; } = string.Empty; - - /// Canonical Agent Plugin media type. - [JsonPropertyName("mediaType")] - public CatalogAgentPluginMediaType MediaType { get; set; } - - /// ISO 8601 timestamp at which the runtime observed the catalog reference. This is not a retrieval or validation timestamp. - [JsonPropertyName("observedAt")] - public string ObservedAt { get; set; } = string.Empty; -} - -/// Syntactically validated GitHub repository provenance declared by catalog metadata. This is a source claim rather than proof that the descriptor URL resolves to the repository. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CatalogPluginRepositorySource -{ - /// Case-preserving safe relative POSIX path derived from metadata.repoPath. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [MaxLength(256)] - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Original runtime-issued operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } - /// Canonical lowercase owner/repository name derived from metadata.sourceSet. - [RegularExpression("^[a-z0-9_.-]+/[a-z0-9_.-]+$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(3)] - [MaxLength(256)] - [JsonPropertyName("repository")] - public string Repository { get; set; } = string.Empty; + /// Immutable terminal receipt. + [JsonPropertyName("outcome")] + public required McpInstallationOutcome Outcome { get; set; } } -/// An inert Agent Plugin catalog result. Its canonical catalog identity, declared version, repository source claim, and explicit compatibility tags are safe to correlate, while its descriptor, URL, raw data, and installed-plugin state remain runtime-private. This contract-only variant does not mint or expose a candidate handle. -/// The plugin variant of . +/// The operation variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogCandidatePlugin : CatalogCandidate +public partial class McpInstallationManagementOutcomeOperation : McpInstallationManagementOutcome { /// [JsonIgnore] - public override string Kind => "plugin"; - - /// Explicit validated compatibility tags, in canonical order. An empty list means the source declared no recognised compatibility; clients must not infer compatibility from other fields. `canvas-only` requires both `canvas` and `github-copilot`. - [JsonPropertyName("compatibilityTags")] - public required IList CompatibilityTags { get; set; } - - /// Description taken verbatim from the card. Inert untrusted text. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("description")] - public string? Description { get; set; } - - /// Display name taken verbatim from the card. Inert untrusted text. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(200)] - [JsonPropertyName("displayName")] - public required string DisplayName { get; set; } - - /// Validated, normalised catalogue resource URN. This identity comes only from the catalog identifier and is never inferred from display text or installed-plugin state. - [JsonPropertyName("identity")] - public required string Identity { get; set; } - - /// Canonical Agent Plugin media type. - [JsonPropertyName("mediaType")] - public required CatalogAgentPluginMediaType MediaType { get; set; } - - /// Where the Agent Plugin catalog reference was observed, without its descriptor, URL, raw data, or content digest. - [JsonPropertyName("provenance")] - public required CatalogAgentPluginCandidateProvenance Provenance { get; set; } - - /// Publisher taken verbatim from the card. Inert untrusted text. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(200)] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("publisher")] - public string? Publisher { get; set; } - - /// Bounded repository provenance declared through the catalog's sourceSet and repoPath metadata. It contains no descriptor URL. - [JsonPropertyName("source")] - public required CatalogPluginRepositorySource Source { get; set; } - - /// Versioned trust metadata observed from the catalog authority. Optional for protocol-3 compatibility and emitted only when the caller also requires the trust-snapshot capability. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("trust")] - public CatalogTrustSnapshot? Trust { get; set; } + public override string Kind => "operation"; - /// Optional version declared by the catalog source. Omitted rather than guessed when the source supplies no version. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("version")] - public string? Version { get; set; } + /// Original-connection operation snapshot. + [JsonPropertyName("operation")] + public required McpInstallationOperationStatus Operation { get; set; } } -/// Authority-reported navigation metadata, returned only to callers requiring catalog-search-pagination and only when a supported token is present. Tokenless first-page and continuation responses omit this object; no counts are inferred from candidates. The opaque token may be retained for previous or numbered navigation even when hasNextPage is false. +/// The refused variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CatalogSearchPagination +public partial class McpInstallationManagementOutcomeRefused : McpInstallationManagementOutcome { - /// One-based page returned by the authority. - [JsonPropertyName("currentPage")] - public int CurrentPage { get; set; } - - /// Whether the authority token advertises a valid next target within the navigation window. Not inferred from token presence, truncated, or currentPage being less than pageCount. - [JsonPropertyName("hasNextPage")] - public bool HasNextPage { get; set; } - - /// Navigation window ceiling ceil(1000 / pageSize), not the number of existing pages. Legal targets must not exceed this ceiling or the token's signed pageCount. - [JsonPropertyName("maxPage")] - public int MaxPage { get; set; } - - /// Backend-reported page count, which may exceed maxPage. Present pagination metadata always describes a multi-page result; zero- and single-page responses omit pagination. Navigation targets must also be within the signed pageCount carried by the supplied token. - [JsonPropertyName("pageCount")] - public long PageCount { get; set; } - - /// Page size bound to the search, equal to the effective request limit. - [JsonPropertyName("pageSize")] - public int PageSize { get; set; } - - /// Opaque pagination token. Session-bound search returns an expiring runtime-owned reference retaining the exact private authority token, original search and authority. Legacy unbound search returns the authority token unchanged, without a runtime-created expiry. Only the runtime unwraps tokens or changes targetPage; SDK consumers must not decode, modify or log them. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [MaxLength(4096)] - [JsonPropertyName("token")] - public string Token { get; set; } = string.Empty; - - /// Backend-reported count for this response, not the number of returned candidates. Its relationship to the full query result set is unknown. - [JsonPropertyName("totalCount")] - public long TotalCount { get; set; } + /// + [JsonIgnore] + public override string Kind => "refused"; - /// The relationship of totalCount to the complete query result set is unknown; neither exactness nor a lower-bound guarantee is implied. - [JsonPropertyName("totalCountRelation")] - public CatalogSearchTotalCountRelation TotalCountRelation { get; set; } + /// Specific bounded refusal. + [JsonPropertyName("reason")] + public required McpInstallationFailureReason Reason { get; set; } } -/// A completed catalog search containing inert candidate summaries. MCP server and AI skill variants carry a single-use handle; the Agent Plugin variant is handleless. -/// The succeeded variant of . +/// The outcome variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSearchResultSucceeded : CatalogSearchResult +public partial class McpInstallationManagementResultOutcome : McpInstallationManagementResult { /// [JsonIgnore] - public override string Kind => "succeeded"; - - /// Matching candidates, never more than the requested limit. All text is inert untrusted data. - [JsonPropertyName("candidates")] - public required IList Candidates { get; set; } + public override string Kind => "outcome"; - /// Protocol version and capabilities the runtime honoured. + /// Capabilities actually honoured for this request. [JsonPropertyName("negotiated")] public required CatalogNegotiatedContract Negotiated { get; set; } - /// Navigation metadata for callers explicitly requiring catalog-search-pagination. Omitted when the authority returns no token, including tokenless first-page and continuation responses. Counts are never substituted from candidates.length. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("pagination")] - public CatalogSearchPagination? Pagination { get; set; } - - /// Pseudonymous identifier for this search, issued by the runtime or by the catalog authority it queried and never by the caller, so it cannot be forged or replayed to attribute an install to a search that never happened. Always present on a success, so a result set can be tied to the installs it leads to. It identifies a search rather than a person: it is derived from no user, account, device, or query data, and must never be joined with user identity to re-identify anyone. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [MaxLength(64)] - [JsonPropertyName("searchId")] - public required string SearchId { get; set; } - - /// Legacy indication that the authority returned a page token. Preserved for compatibility; this is not a has-next-page indicator. Use pagination.hasNextPage when pagination metadata is present. - [JsonPropertyName("truncated")] - public required bool Truncated { get; set; } + /// Observed management outcome. + [JsonPropertyName("outcome")] + public required McpInstallationManagementOutcome Outcome { get; set; } } /// The caller's protocol version or required capabilities cannot be honoured. Returned instead of a partial or ambiguous success. -/// The negotiation-refused variant of . +/// The negotiation-refused variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSearchResultNegotiationRefused : CatalogSearchResult +public partial class McpInstallationManagementResultNegotiationRefused : McpInstallationManagementResult { /// [JsonIgnore] @@ -5796,34 +6132,10 @@ public partial class CatalogSearchResultNegotiationRefused : CatalogSearchResult public required IList UnsupportedCapabilities { get; set; } } -/// The request asked for a candidate kind this runtime does not serve. -/// The unsupported-kind variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSearchResultUnsupportedKind : CatalogSearchResult -{ - /// - [JsonIgnore] - public override string Kind => "unsupported-kind"; - - /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } - - /// The kinds from the request that are not supported. - [JsonPropertyName("requestedKinds")] - public required IList RequestedKinds { get; set; } - - /// Every candidate kind this runtime can serve. - [JsonPropertyName("supportedKinds")] - public required IList SupportedKinds { get; set; } -} - /// The request was rejected because a bounded field fell outside its permitted range or a required field was unusable. Pagination may also be rejected by the authority after a continuation request; repeat the search without page. -/// The invalid-request variant of . +/// The invalid-request variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSearchResultInvalidRequest : CatalogSearchResult +public partial class McpInstallationManagementResultInvalidRequest : McpInstallationManagementResult { /// [JsonIgnore] @@ -5840,11484 +6152,11985 @@ public partial class CatalogSearchResultInvalidRequest : CatalogSearchResult public required string Message { get; set; } } -/// An optional catalog authentication exchange did not establish the caller's identity. Anonymous search remains supported; this refusal is reserved for an operation that cannot continue after the attempted exchange. It is distinct from `policy-rejected` and from a network failure, and the reason identifies the recovery action. -/// The authentication-required variant of . +/// One Registry string-valued configuration entry for the selected transport. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSearchResultAuthenticationRequired : CatalogSearchResult +public sealed class McpInstallationInput { - /// - [JsonIgnore] - public override string Kind => "authentication-required"; + /// Exact category declared by the selected choice. + [JsonPropertyName("category")] + public McpPlanValueCategory Category { get; set; } - /// Human-readable explanation, safe to surface. Never contains a credential or token, nor a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Exact key declared by the selected choice. + [JsonPropertyName("key")] + public string Key { get; set; } = string.Empty; - /// Why authentication failed. Only an expired credential justifies attempting a silent refresh; an absent or rejected credential requires sign-in. - [JsonPropertyName("reason")] - public required CatalogAuthenticationRequiredReason Reason { get; set; } + /// Explicit non-secret value. Secret placeholders use a separate input channel. + [JsonPropertyName("value")] + public string Value { get; set; } = string.Empty; } -/// Registry or enterprise policy refused the operation. -/// The policy-rejected variant of . +/// A request-local value for one exact reviewed placeholder. Never logged or persisted in a plan. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSearchResultPolicyRejected : CatalogSearchResult +public sealed class McpInstallationSecret { - /// - [JsonIgnore] - public override string Kind => "policy-rejected"; - - /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Exact placeholder from the selected choice, not a caller-chosen backend identifier. + [JsonPropertyName("placeholder")] + public string Placeholder { get; set; } = string.Empty; - /// Which authority produced the decision. - [JsonPropertyName("source")] - public required McpPlanPolicySource Source { get; set; } + /// Fresh explicit secret value. It is omitted from confirmation reviews and telemetry. + [JsonPropertyName("value")] + public string Value { get; set; } = string.Empty; } -/// The runtime could not reach the catalog authority or retrieve a card. Covers being offline as well as transport-level failure. -/// The network-failure variant of . +/// Side-effect-free preparation of one original bound remote MCP choice. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSearchResultNetworkFailure : CatalogSearchResult +internal sealed class McpPrepareInstallRequest { - /// - [JsonIgnore] - public override string Kind => "network-failure"; + /// Exact selected alternative from that plan. + [JsonPropertyName("choiceId")] + public string ChoiceId { get; set; } = string.Empty; - /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Required bound catalogue and confirmed remote installation capabilities. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); set; } - /// Categorised failure, low cardinality so it can be aggregated without carrying a URL. - [JsonPropertyName("reason")] - public required CatalogNetworkFailureReason Reason { get; set; } + /// + /// Declared non-secret values. Non-empty only when the caller requires + /// `mcp-configured-remote-installation`; omitted values use the card default. + /// + [JsonPropertyName("inputs")] + public IList Inputs { get => field ??= []; set; } - /// Bounded cooldown in seconds before another catalog request should be attempted, when the authority supplied a numeric Retry-After value or the runtime applied its documented fallback. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("retryAfterSeconds")] - public int? RetryAfterSeconds { get; set; } + /// Original single-use bound plan, never a client-authored configuration. + [JsonPropertyName("planHandle")] + public string PlanHandle { get; set; } = string.Empty; - /// HTTP status code, when the failure was a rejected response. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("statusCode")] - public int? StatusCode { get; set; } + /// An existing local session attached to this connection, not permission to attach one. + [JsonPropertyName("policySessionId")] + public string PolicySessionId { get; set; } = string.Empty; + + /// + /// One entry per declared secret placeholder of the selected choice. Non-empty + /// only when the caller requires `mcp-configured-remote-installation`. + /// + [JsonPropertyName("secrets")] + public IList Secrets { get => field ??= []; set; } + + /// The trusted host presents this choice alongside the exact secret placeholders. + [JsonPropertyName("secretStorage")] + public McpInstallationSecretStorage SecretStorage { get; set; } + + /// The exact original source, used transiently only after confirmation. + [JsonPropertyName("source")] + public McpServerCardReference Source { get => field ??= new(); set; } } -/// Retrieval was refused by the runtime's hardened fetch boundary before any request left the process, or before a redirect was followed. -/// The unsafe-retrieval variant of . +/// An installation result together with the exact honoured contract, or a negotiation refusal. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSearchResultUnsafeRetrieval : CatalogSearchResult +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(McpInstallationResultOutcome), "outcome")] +[JsonDerivedType(typeof(McpInstallationResultNegotiationRefused), "negotiation-refused")] +[JsonDerivedType(typeof(McpInstallationResultInvalidRequest), "invalid-request")] +public partial class McpInstallationResult +{ + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} + + +/// The outcome variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class McpInstallationResultOutcome : McpInstallationResult { /// [JsonIgnore] - public override string Kind => "unsafe-retrieval"; + public override string Kind => "outcome"; - /// Human-readable explanation, safe to surface. Never contains the refused URL, nor a query, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Capabilities actually honoured for this request. + [JsonPropertyName("negotiated")] + public required CatalogNegotiatedContract Negotiated { get; set; } - /// Which control refused the retrieval, low cardinality so it can be aggregated without carrying a URL. - [JsonPropertyName("reason")] - public required CatalogUnsafeRetrievalReason Reason { get; set; } + /// Terminal result of the original operation. + [JsonPropertyName("outcome")] + public required McpInstallationOutcome Outcome { get; set; } } -/// A card could not be parsed or did not satisfy its declared media type's schema. -/// The malformed-card variant of . +/// The caller's protocol version or required capabilities cannot be honoured. Returned instead of a partial or ambiguous success. +/// The negotiation-refused variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSearchResultMalformedCard : CatalogSearchResult +public partial class McpInstallationResultNegotiationRefused : McpInstallationResult { /// [JsonIgnore] - public override string Kind => "malformed-card"; - - /// Media type the card was interpreted as, when it declared one this runtime recognises. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("mediaType")] - public CatalogMediaType? MediaType { get; set; } + public override string Kind => "negotiation-refused"; - /// Human-readable explanation, safe to surface. Never echoes card content, nor a query, URL, handle, or secret. + /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] [MaxLength(1000)] [JsonPropertyName("message")] public required string Message { get; set; } - /// How the card failed validation. + /// Lowest caller protocol version this runtime will serve. + [JsonPropertyName("minimumSupportedProtocolVersion")] + public required long MinimumSupportedProtocolVersion { get; set; } + + /// Whether the version or the capability set was the problem. [JsonPropertyName("reason")] - public required CatalogMalformedCardReason Reason { get; set; } + public required CatalogNegotiationRefusedReason Reason { get; set; } + + /// Protocol version of the runtime that refused the request. + [JsonPropertyName("runtimeProtocolVersion")] + public required long RuntimeProtocolVersion { get; set; } + + /// Capabilities this runtime can safely advertise to this caller. The complete five-capability protocol-3 legacy set is always present; every capability added after that baseline appears only when the caller required it, so an older closed-enum decoder can still consume a refusal. This list does not imply that every deployment has enabled every operation. + [JsonPropertyName("supportedCapabilities")] + public required IList SupportedCapabilities { get; set; } + + /// The subset of the caller's bounded extensible capability identifiers this runtime cannot honour. + [JsonPropertyName("unsupportedCapabilities")] + public required IList UnsupportedCapabilities { get; set; } } -/// An upstream catalog response broke the wire contract. Most importantly, every result must carry exactly one of a URL or embedded data: a result carrying both, or neither, is refused here rather than being guessed at. -/// The contract-violation variant of . +/// The request was rejected because a bounded field fell outside its permitted range or a required field was unusable. Pagination may also be rejected by the authority after a continuation request; repeat the search without page. +/// The invalid-request variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSearchResultContractViolation : CatalogSearchResult +public partial class McpInstallationResultInvalidRequest : McpInstallationResult { /// [JsonIgnore] - public override string Kind => "contract-violation"; + public override string Kind => "invalid-request"; - /// Human-readable explanation, safe to surface. Never echoes response content, nor a query, URL, handle, or secret. + /// Which request field was rejected. + [JsonPropertyName("field")] + public required CatalogInvalidRequestField Field { get; set; } + + /// Human-readable explanation, safe to surface. Never echoes the offending value, nor a query, URL, handle, or secret. [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] [MaxLength(1000)] [JsonPropertyName("message")] public required string Message { get; set; } - - /// Which rule the response broke. - [JsonPropertyName("reason")] - public required CatalogContractViolationReason Reason { get; set; } } -/// The operation is not available on this runtime. Distinct from a network failure: nothing was attempted. -/// The unavailable variant of . +/// Applies exactly one previously prepared operation on its original connection. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSearchResultUnavailable : CatalogSearchResult +internal sealed class McpApplyInstallRequest { - /// - [JsonIgnore] - public override string Kind => "unavailable"; + /// Capabilities required by the original prepared operation. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); set; } - /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Runtime-issued ID already returned by prepareInstall, never reused or rebound. + [JsonPropertyName("operationId")] + public string OperationId { get; set; } = string.Empty; - /// Why the operation is unavailable. - [JsonPropertyName("reason")] - public required CatalogUnavailableReason Reason { get; set; } + /// Same existing attached or privately borrowed session as preparation. + [JsonPropertyName("policySessionId")] + public string PolicySessionId { get; set; } = string.Empty; } -/// An explicit numbered-page request. SDK consumers treat the token as opaque. For bound search, the runtime unwraps an expiring owner-bound reference to the private authority token; only the runtime changes the authority token's targetPage. Legacy unbound navigation keeps its authority-issued token semantics. No snapshot stability is promised. +/// Read-only preparation of one owned removal under fresh selected-session authority. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CatalogSearchPage +internal sealed class McpPlanUninstallRequest { - /// Requested one-based page. Must not exceed either the token's signed pageCount or the navigation window ceil(1000 / pageSize). Repeat the search without page to discover newly available pages beyond that signed pageCount. - [JsonPropertyName("number")] - public int Number { get; set; } + /// Required authenticated bound installation capabilities. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); set; } - /// Opaque pagination token from an earlier response, owner-bound when session-bound search was requested. Never decode, modify or log it in an SDK consumer. Expired or foreign bound references require a fresh bound search, not a legacy retry. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [MaxLength(4096)] - [JsonPropertyName("token")] - public string Token { get; set; } = string.Empty; + /// Exact receipt to inspect, not a server-name guess. + [JsonPropertyName("installationId")] + public string InstallationId { get; set; } = string.Empty; + + /// Existing selected session on the original connection. + [JsonPropertyName("policySessionId")] + public string PolicySessionId { get; set; } = string.Empty; } -/// A bounded catalog search. Both the query length and the result count are capped by the schema so a caller cannot request an unbounded scan. +/// One-use application of the exact retained removal plan. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CatalogSearchRequest +internal sealed class McpApplyUninstallRequest { - /// Protocol version and capabilities the caller requires. + /// Required authenticated bound installation capabilities. [JsonPropertyName("contract")] - public required CatalogClientContract Contract { get; set; } + public CatalogClientContract Contract { get => field ??= new(); set; } - /// Restrict results to these candidate kinds. Agent Plugins are opt-in and require the `agent-plugin-discovery` capability so protocol-v3 clients generated before that variant cannot receive an unknown result; when omitted, the backwards-compatible MCP server and AI skill kinds are searched. - [JsonPropertyName("kinds")] - public IList? Kinds { get; set; } + /// Opaque original removal plan, consumed once. + [JsonPropertyName("planHandle")] + public string PlanHandle { get; set; } = string.Empty; - /// Maximum number of candidates to return. Defaults to 10 when omitted. - [JsonPropertyName("limit")] - public int? Limit { get; set; } - - /// Numbered navigation using metadata from an earlier response. Requires catalog-search-pagination and the same query, kinds and effective limit. Omit for a fresh first-page search. - [JsonPropertyName("page")] - public CatalogSearchPage? Page { get; set; } - - /// - /// Select an existing attached local session. Requires authenticated, session-bound search. - /// The runtime never creates, resumes or reconfigures a session to honour this selector. - /// - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [MaxLength(256)] + /// Same existing selected session as removal preparation. [JsonPropertyName("policySessionId")] - public string? PolicySessionId { get; set; } - - /// Free-text search query. Persisted as tool input for session continuity, but omitted from telemetry. - [RegularExpression("\\S")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [MaxLength(256)] - [JsonPropertyName("query")] - public required string Query { get; set; } + public string PolicySessionId { get; set; } = string.Empty; } -/// Typed outcome of catalog.select. Only the selected host result carries a fresh candidate handle; the model-facing projection removes both that handle and searchId. -/// Polymorphic base type discriminated by kind. +/// User-configured MCP servers, keyed by server name. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(CatalogSelectionResultSelected), "selected")] -[JsonDerivedType(typeof(CatalogSelectionResultDeclined), "declined")] -[JsonDerivedType(typeof(CatalogSelectionResultCancelled), "cancelled")] -[JsonDerivedType(typeof(CatalogSelectionResultTimedOut), "timed-out")] -[JsonDerivedType(typeof(CatalogSelectionResultInvalid), "invalid")] -[JsonDerivedType(typeof(CatalogSelectionResultStale), "stale")] -[JsonDerivedType(typeof(CatalogSelectionResultReplayed), "replayed")] -[JsonDerivedType(typeof(CatalogSelectionResultForeign), "foreign")] -[JsonDerivedType(typeof(CatalogSelectionResultWrongKind), "wrong-kind")] -[JsonDerivedType(typeof(CatalogSelectionResultNegotiationRefused), "negotiation-refused")] -[JsonDerivedType(typeof(CatalogSelectionResultInvalidRequest), "invalid-request")] -[JsonDerivedType(typeof(CatalogSelectionResultUnavailable), "unavailable")] -public partial class CatalogSelectionResult +public sealed class McpConfigList { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; + /// All MCP servers from user config, keyed by name. + [JsonPropertyName("servers")] + public IDictionary Servers { get => field ??= new Dictionary(); set; } } - -/// The chosen candidate was transferred into a fresh bounded single-use handle for a later explicit planning request. -/// The selected variant of . +/// MCP server name and configuration to add to user configuration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSelectionResultSelected : CatalogSelectionResult +internal sealed class McpConfigAddRequest { - /// - [JsonIgnore] - public override string Kind => "selected"; - - /// Fresh single-use candidate handle accepted by mcp.planInstall. Returned only to the native host and never included in model-tool output. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [MaxLength(256)] - [JsonPropertyName("candidateHandle")] - public required string CandidateHandle { get; set; } + /// MCP server configuration (stdio process or remote HTTP/SSE). + [JsonPropertyName("config")] + public JsonElement Config { get; set; } - /// The exact search identifier privately bound to the selected candidate. + /// Unique name for the MCP server. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] [MinLength(1)] - [MaxLength(64)] - [JsonPropertyName("searchId")] - public required string SearchId { get; set; } + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// The caller explicitly declined every candidate and the retained search state was released. -/// The declined variant of . +/// MCP server name and replacement configuration to write to user configuration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSelectionResultDeclined : CatalogSelectionResult +internal sealed class McpConfigUpdateRequest { - /// - [JsonIgnore] - public override string Kind => "declined"; + /// MCP server configuration (stdio process or remote HTTP/SSE). + [JsonPropertyName("config")] + public JsonElement Config { get; set; } - /// The search identifier privately bound to the released selection group. + /// Name of the MCP server to update. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] [MinLength(1)] - [MaxLength(64)] - [JsonPropertyName("searchId")] - public required string SearchId { get; set; } + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// The caller cancelled the selection interaction and the retained search state was released. -/// The cancelled variant of . +/// MCP server name to remove from user configuration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSelectionResultCancelled : CatalogSelectionResult +internal sealed class McpConfigRemoveRequest { - /// - [JsonIgnore] - public override string Kind => "cancelled"; + /// OAuth Client ID Metadata Document URL whose persisted credentials should also be removed. + [JsonPropertyName("authClientIdMetadataUrl")] + public string? AuthClientIdMetadataUrl { get; set; } - /// The search identifier privately bound to the released selection group. + /// Name of the MCP server to remove. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] [MinLength(1)] - [MaxLength(64)] - [JsonPropertyName("searchId")] - public required string SearchId { get; set; } + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// The host declared that its live selection interaction timed out, and the retained search state was released. -/// The timed-out variant of . +/// MCP server names to enable for new sessions. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSelectionResultTimedOut : CatalogSelectionResult +internal sealed class McpConfigEnableRequest { - /// - [JsonIgnore] - public override string Kind => "timed-out"; + /// Names of MCP servers to enable. Each server is removed from the persisted disabled list so new sessions spawn it. Unknown or already-enabled names are ignored. + [JsonPropertyName("names")] + public IList Names { get => field ??= []; set; } +} - /// The search identifier privately bound to the released selection group. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [MaxLength(64)] - [JsonPropertyName("searchId")] - public required string SearchId { get; set; } +/// MCP server names to disable for new sessions. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpConfigDisableRequest +{ + /// Names of MCP servers to disable. Each server is added to the persisted disabled list so new sessions skip it. Already-disabled names are ignored. Active sessions keep their current connections until they end. + [JsonPropertyName("names")] + public IList Names { get => field ??= []; set; } } -/// The selection reference was malformed or unknown. -/// The invalid variant of . +/// Request id naming a cancellable registry search. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSelectionResultInvalid : CatalogSelectionResult +internal sealed class McpRegistryRequestIdResult { - /// - [JsonIgnore] - public override string Kind => "invalid"; + /// Request ID for `mcp.registry.search` and `mcp.registry.cancel`. It serves one search. Allocation can reclaim unused IDs at the 1,024-ID reservation limit. + [JsonPropertyName("requestId")] + public long RequestId { get; set; } +} - /// Human-readable explanation safe to surface. Never contains the presented reference or private candidate state. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } +/// Servers selected from the registry response. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpRegistrySearchResult +{ + /// The server objects, carried opaquely. The runtime follows pages, keeps the newest entry per server name, cuts the list to `limit`, and sorts an empty-query result by GitHub stars. Each server object remains unchanged because the registry owns that shape. + [JsonPropertyName("servers")] + public JsonElement Servers { get; set; } } -/// The runtime-enforced selection reference lifetime elapsed before the request arrived. -/// The stale variant of . +/// Registry search terms, the credential to search under, and the request id that makes the search cancellable. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSelectionResultStale : CatalogSelectionResult +internal sealed class McpRegistrySearchRequest { - /// - [JsonIgnore] - public override string Kind => "stale"; + /// The credential the search runs under, carried opaquely. Hosts usually send credential-free `AuthIdentity`; a `token` identity can be resolved only when it embeds a token, while `env` and `gh-cli` identities can use a token embedded in the request first. + [JsonPropertyName("authInfo")] + public JsonElement AuthInfo { get; set; } - /// Human-readable explanation safe to surface. Never contains the presented reference or private candidate state. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Maximum number of servers to return. + [JsonPropertyName("limit")] + public long Limit { get; set; } + + /// Free-text query. Omitted or empty asks the registry for its top servers rather than searching. A value that is not a string is refused. + [JsonPropertyName("query")] + public string? Query { get; set; } + + /// Repository used for the policy lookup, as `owner/name`. The policy selects the registry URL and whether the user token goes to the registry. The registry receives this repository only when the policy entry lists it as required context. A value that is not a string is refused. + [JsonPropertyName("repository")] + public string? Repository { get; set; } + + /// Request ID from `mcp.registry.allocateRequestId`. The search refuses unknown, reclaimed, or canceled IDs and IDs that another search or request already uses. + [JsonPropertyName("requestId")] + public long RequestId { get; set; } } -/// The selection group was already terminated or its pending host hand-off was already claimed. -/// The replayed variant of . +/// Whether the cancel reached a live search. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSelectionResultReplayed : CatalogSelectionResult +internal sealed class McpRegistryCancelResult { - /// - [JsonIgnore] - public override string Kind => "replayed"; + /// True when the cancel stopped a running search. False for unknown or reclaimed IDs, completed or canceled searches, and unused reservations. The cancel releases an unused reservation. + [JsonPropertyName("canceled")] + public bool Canceled { get; set; } +} - /// Human-readable explanation safe to surface. Never contains the presented reference or private candidate state. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } +/// Registry search to abandon. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpRegistryCancelRequest +{ + /// Request ID from `mcp.registry.allocateRequestId` that the search uses. + [JsonPropertyName("requestId")] + public long RequestId { get; set; } } -/// The selection reference belongs to another runtime instance or session. -/// The foreign variant of . +/// New-work inventory or recovery request under an explicitly selected existing session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSelectionResultForeign : CatalogSelectionResult +internal sealed class McpInstallationsRequest { - /// - [JsonIgnore] - public override string Kind => "foreign"; + /// Required authenticated bound installation contract. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); set; } - /// Human-readable explanation safe to surface. Never contains the presented reference or private candidate state. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Existing selected local session on this connection. + [JsonPropertyName("policySessionId")] + public string PolicySessionId { get; set; } = string.Empty; } -/// The presented opaque handle was issued for another catalog operation. -/// The wrong-kind variant of . +/// Existing-operation control. A new session selector is deliberately not accepted. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSelectionResultWrongKind : CatalogSelectionResult +internal sealed class McpInstallationOperationRequest { - /// - [JsonIgnore] - public override string Kind => "wrong-kind"; + /// Original installation wire capability; new-work authentication is not reacquired. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); set; } - /// Human-readable explanation safe to surface. Never contains the presented reference or private candidate state. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Exact runtime-issued operation ID on the original connection. + [JsonPropertyName("operationId")] + public string OperationId { get; set; } = string.Empty; } -/// The caller's protocol version or required capabilities cannot be honoured. Returned instead of a partial or ambiguous success. -/// The negotiation-refused variant of . +/// Installed plugin that contributes a discovered extension. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSelectionResultNegotiationRefused : CatalogSelectionResult +public sealed class DiscoveredExtensionPlugin { - /// - [JsonIgnore] - public override string Kind => "negotiation-refused"; + /// Installed plugin name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; +} - /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } +/// Discovered extension metadata and persistent enablement state. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class DiscoveredExtension +{ + /// Whether this extension's persistent per-ID preference is enabled. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } - /// Lowest caller protocol version this runtime will serve. - [JsonPropertyName("minimumSupportedProtocolVersion")] - public required long MinimumSupportedProtocolVersion { get; set; } + /// Source-qualified ID accepted by both server and session extension enablement methods. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// Whether the version or the capability set was the problem. - [JsonPropertyName("reason")] - public required CatalogNegotiationRefusedReason Reason { get; set; } + /// Human-readable extension name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Protocol version of the runtime that refused the request. - [JsonPropertyName("runtimeProtocolVersion")] - public required long RuntimeProtocolVersion { get; set; } + /// Absolute path to the extension entry module, suitable for revealing it in a file manager. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; - /// Capabilities this runtime can safely advertise to this caller. The complete five-capability protocol-3 legacy set is always present; every capability added after that baseline appears only when the caller required it, so an older closed-enum decoder can still consume a refusal. This list does not imply that every deployment has enabled every operation. - [JsonPropertyName("supportedCapabilities")] - public required IList SupportedCapabilities { get; set; } + /// Containing plugin metadata for plugin-contributed extensions. + [JsonPropertyName("plugin")] + public DiscoveredExtensionPlugin? Plugin { get; set; } - /// The subset of the caller's bounded extensible capability identifiers this runtime cannot honour. - [JsonPropertyName("unsupportedCapabilities")] - public required IList UnsupportedCapabilities { get; set; } + /// Discovery source. + [JsonPropertyName("source")] + public DiscoveredExtensionSource Source { get; set; } } -/// The request was rejected because a bounded field fell outside its permitted range or a required field was unusable. Pagination may also be rejected by the authority after a continuation request; repeat the search without page. -/// The invalid-request variant of . +/// Extensions discovered from persisted Copilot home state and their effective loading mode. Launch-scoped additional plugins are not included. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSelectionResultInvalidRequest : CatalogSelectionResult +public sealed class DiscoveredExtensions { - /// - [JsonIgnore] - public override string Kind => "invalid-request"; - - /// Which request field was rejected. - [JsonPropertyName("field")] - public required CatalogInvalidRequestField Field { get; set; } + /// Discovered user and enabled installed-plugin extensions from persisted Copilot home state. + [JsonPropertyName("extensions")] + public IList Extensions { get => field ??= []; set; } - /// Human-readable explanation, safe to surface. Never echoes the offending value, nor a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Effective extension loading mode. Defaults to load_and_augment when unset. + [JsonPropertyName("mode")] + public DiscoveredExtensionMode Mode { get; set; } } -/// The operation is not available on this runtime. Distinct from a network failure: nothing was attempted. -/// The unavailable variant of . +/// Source-qualified extension identifiers to persistently enable for future sessions. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSelectionResultUnavailable : CatalogSelectionResult +internal sealed class DiscoveredExtensionsEnableRequest { - /// - [JsonIgnore] - public override string Kind => "unavailable"; - - /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(1000)] - [JsonPropertyName("message")] - public required string Message { get; set; } - - /// Why the operation is unavailable. - [JsonPropertyName("reason")] - public required CatalogUnavailableReason Reason { get; set; } + /// Source-qualified user or plugin extension IDs to enable. + [JsonPropertyName("ids")] + public IList Ids { get => field ??= []; set; } } -/// Terminates one retained catalog selection group through an opaque reference previously returned by the model-safe search projection. +/// Source-qualified extension identifiers to persistently disable for future sessions. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CatalogSelectionRequest +internal sealed class DiscoveredExtensionsDisableRequest { - /// Protocol version and capabilities the caller requires. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); set; } - - /// The terminal outcome declared by the caller. Timed-out means the host's live interaction deadline elapsed; a reference whose runtime TTL elapsed is rejected separately as stale. - [JsonPropertyName("outcome")] - public CatalogSelectionDecision Outcome { get; set; } - - /// Opaque runtime-instance scoped reference to one visible candidate. For a non-selected outcome, any candidate reference from the same search closes that search's retained group. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [MaxLength(256)] - [JsonPropertyName("selectionRef")] - public string SelectionRef { get; set; } = string.Empty; + /// Source-qualified user or plugin extension IDs to disable. + [JsonPropertyName("ids")] + public IList Ids { get => field ??= []; set; } +} - /// Locally owned root session whose retained search state is being resolved. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [MaxLength(256)] - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; +/// The prompt the init command runs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class InstructionsInitPromptResult +{ + /// Prompt text authoring repository instructions. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; } -/// Information about an installed plugin tracked in global state. +/// Loaded instruction source for a session, including path, content, category, location, applicability, and optional description. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class InstalledPluginInfo +public sealed class InstructionSource { - /// Opaque, stable hash identifying a direct (non-marketplace) install source. Present only for direct repo / URL / local installs; absent for marketplace plugins. Same source yields the same id; distinct sources never collide. - [JsonPropertyName("directSourceId")] - public string? DirectSourceId { get; set; } + /// Glob pattern(s) from frontmatter — when set, this instruction applies only to matching files. + [JsonPropertyName("applyTo")] + public IList? ApplyTo { get; set; } - /// Whether the plugin is currently enabled for new sessions. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } + /// Raw content of the instruction file. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; - /// Whether the managed desired plugin currently has an installed or live record. Set to false for a managed desired entry retained in the listing after installation or reconciliation failed. - [JsonPropertyName("installed")] - public bool? Installed { get; set; } + /// When true, this source starts disabled and must be toggled on by the user. + [JsonPropertyName("defaultDisabled")] + public bool? DefaultDisabled { get; set; } - /// Absolute path of the marketplace directory a live plugin was resolved from. Present only on live, never-persisted records — a plugin belonging to a directory/local marketplace, which is loaded from its real directory on every pass instead of a copy under the installed-plugins cache. Its presence is what marks a listed plugin as live: such a plugin is always present on disk, so `enabled` is its only meaningful state and it is never "not installed". - [JsonPropertyName("installedFrom")] - public string? InstalledFrom { get; set; } + /// Short description (body after frontmatter) for use in instruction tables. + [JsonPropertyName("description")] + public string? Description { get; set; } - /// Whether enterprise managed settings control this plugin's enabled state. - [JsonPropertyName("managed")] - public bool? Managed { get; set; } + /// Unique identifier for this source (used for toggling). + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// The enabled state required by enterprise managed settings, when this plugin spec is managed. - [JsonPropertyName("managedDesiredEnabled")] - public bool? ManagedDesiredEnabled { get; set; } + /// Human-readable label. + [JsonPropertyName("label")] + public string Label { get; set; } = string.Empty; - /// Marketplace the plugin came from. Empty string ("") for direct repo / URL / local installs. - [JsonPropertyName("marketplace")] - public string Marketplace { get; set; } = string.Empty; + /// Where this source lives — used for UI grouping. + [JsonPropertyName("location")] + public InstructionSourceLocation Location { get; set; } - /// Plugin name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// The project path this source was discovered from. Only set by sessionless discovery for repository, working-directory, and project-scoped plugin sources, where it disambiguates sources across multiple workspace roots. The session-scoped getSources leaves it unset. + [JsonPropertyName("projectPath")] + public string? ProjectPath { get; set; } - /// Runtime-reported plugin provenance. Currently set to "builtin" only for plugins registered through the trusted host built-in boundary; absent for installed, marketplace, direct, and live plugins. - [JsonPropertyName("source")] - public string? Source { get; set; } + /// File path relative to repo or absolute for home. + [JsonPropertyName("sourcePath")] + public string SourcePath { get; set; } = string.Empty; - /// Installed version (when reported by the plugin manifest). - [JsonPropertyName("version")] - public string? Version { get; set; } + /// Category of instruction source — used for merge logic. + [JsonPropertyName("type")] + public InstructionSourceType Type { get; set; } } -/// Plugins installed in user/global state. +/// Instruction sources discovered across user, repository, and plugin sources. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PluginListResult +public sealed class ServerInstructionSourceList { - /// Installed plugins. - [JsonPropertyName("plugins")] - public IList Plugins { get => field ??= []; set; } + /// All discovered instruction sources. + [JsonPropertyName("sources")] + public IList Sources { get => field ??= []; set; } } -/// Result of installing a plugin. +/// Optional project paths to include in instruction discovery. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PluginInstallResult +public sealed class InstructionsDiscoverRequest { - /// Set when the install path is deprecated (e.g. direct repo / URL / local installs). Callers should surface this to end users. - [JsonPropertyName("deprecationWarning")] - public string? DeprecationWarning { get; set; } - - /// The newly installed plugin's metadata. - [JsonPropertyName("plugin")] - public InstalledPluginInfo Plugin { get => field ??= new(); set; } - - /// Optional post-install message provided by the plugin (e.g. setup instructions). - [JsonPropertyName("postInstallMessage")] - public string? PostInstallMessage { get; set; } + /// When true, omit the host's instruction sources (user/home-level files and plugin rules), leaving only repository and working-directory sources. For multitenant deployments. + [JsonPropertyName("excludeHostInstructions")] + public bool? ExcludeHostInstructions { get; set; } - /// Number of skills discovered and installed from the plugin. - [JsonPropertyName("skillsInstalled")] - public long SkillsInstalled { get; set; } + /// Explicit discovery-only instruction roots using existing SDK instruction formats, not filesystem grants. Available without ambient discovery, subject to instruction/managed disables. Relative paths resolve against the request's effective working directory when supplied. Null and omission are equivalent; an empty array contributes no explicit roots. + [JsonPropertyName("instructionDirectories")] + public IList? InstructionDirectories { get; set; } - /// Where the completed plugin tree was staged before atomic promotion. - [JsonPropertyName("stagingMode")] - public PluginInstallStagingMode? StagingMode { get; set; } + /// Optional list of project directory paths to scan for repository/working-directory instruction sources. When omitted or empty, only user-level and plugin instruction sources are returned (no project scan). + [JsonPropertyName("projectPaths")] + public IList? ProjectPaths { get; set; } } -/// Plugin source and optional working directory for relative-path resolution. +/// Explicit discovery arguments for one location, supplied by the caller rather than derived from a session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsInstallRequest +internal sealed class InstructionsDiscoverRepoSourcesRequest { - /// Plugin install spec. Accepts the same forms as the CLI: "plugin@marketplace" (marketplace install), "owner/repo" or "owner/repo:subpath" (GitHub direct), an http/https/ssh URL, or a local path. Direct (non-marketplace) installs are deprecated and will produce a deprecationWarning in the result. - [JsonPropertyName("source")] - public string Source { get; set; } = string.Empty; - - /// Working directory used to resolve relative local paths in `source`. Defaults to the server's current working directory. - [JsonPropertyName("workingDirectory")] - public string? WorkingDirectory { get; set; } -} + /// Extra directories to scan alongside instruction directories configured in the runtime environment. + [JsonPropertyName("additionalDirs")] + public IList? AdditionalDirs { get; set; } -/// Name (or spec) of the plugin to uninstall. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsUninstallRequest -{ - /// Stable source identity for a direct (non-marketplace) install. Disambiguates uninstall when multiple installed plugins share the same name. - [JsonPropertyName("directSourceId")] - public string? DirectSourceId { get; set; } + /// Copilot configuration directory for user-level sources. An omitted or empty value uses the server's configuration home. + [JsonPropertyName("configDir")] + public string? ConfigDir { get; set; } - /// Plugin name or "plugin@marketplace" spec to uninstall. When ambiguous, prefer the fully-qualified spec. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; -} + /// Working directory whose instruction sources should also be included. Omission adds no separate working-directory scope. + [JsonPropertyName("cwdPath")] + public string? CwdPath { get; set; } -/// Result of updating a single plugin. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PluginUpdateResult -{ - /// Version after the update, when reported by the plugin manifest. - [JsonPropertyName("newVersion")] - public string? NewVersion { get; set; } + /// When true, also discover instruction files nested below the location. + [JsonPropertyName("enableChildInstructions")] + public bool? EnableChildInstructions { get; set; } - /// Version that was previously installed, when available. - [JsonPropertyName("previousVersion")] - public string? PreviousVersion { get; set; } + /// When true, include the sources a command-line host reads. Hosts that are not the CLI omit it. + [JsonPropertyName("isCli")] + public bool? IsCli { get; set; } - /// Number of skills discovered and installed after the update. - [JsonPropertyName("skillsInstalled")] - public long SkillsInstalled { get; set; } + /// Repository location to discover. An empty string selects no repository scope; cwdPath can still supply working-directory sources. + [JsonPropertyName("location")] + public string Location { get; set; } = string.Empty; } -/// Name (or spec) of the plugin to update. +/// Canonical file or directory where custom instructions can be discovered or created, with location, kind, preference, and project path. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsUpdateRequest +public sealed class InstructionDiscoveryPath { - /// Plugin name or "plugin@marketplace" spec to update. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; -} - -/// Per-plugin result from updating all plugins, with versions, skills installed, success flag, and optional error. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PluginUpdateAllEntry -{ - /// Error message (failure only). - [JsonPropertyName("error")] - public string? Error { get; set; } - - /// Marketplace the plugin came from. Empty string ("") for direct installs. - [JsonPropertyName("marketplace")] - public string Marketplace { get; set; } = string.Empty; - - /// Plugin name that was updated. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Whether the target is a single file or a directory of instruction files. + [JsonPropertyName("kind")] + public InstructionDiscoveryPathKind Kind { get; set; } - /// Version after the update, when available. - [JsonPropertyName("newVersion")] - public string? NewVersion { get; set; } + /// Which tier this target belongs to. + [JsonPropertyName("location")] + public InstructionDiscoveryPathLocation Location { get; set; } - /// Previously installed version, when available. - [JsonPropertyName("previousVersion")] - public string? PreviousVersion { get; set; } + /// Absolute path of the file or directory (may not exist on disk yet). + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; - /// Number of skills installed after the update (success only). - [JsonPropertyName("skillsInstalled")] - public long? SkillsInstalled { get; set; } + /// Whether this is the canonical target to create new instructions in its tier. At most one entry per tier is preferred. + [JsonPropertyName("preferredForCreation")] + public bool PreferredForCreation { get; set; } - /// Whether the update succeeded for this plugin. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// The input project path this target was derived from (only for repository targets). + [JsonPropertyName("projectPath")] + public string? ProjectPath { get; set; } } -/// Result of updating all installed plugins. +/// Canonical files and directories where custom instructions can be created so the runtime will recognize them. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PluginUpdateAllResult +public sealed class InstructionDiscoveryPathList { - /// Per-plugin update results in deterministic order. - [JsonPropertyName("results")] - public IList Results { get => field ??= []; set; } + /// Canonical instruction create/discovery files and directories, in priority order. + [JsonPropertyName("paths")] + public IList Paths { get => field ??= []; set; } } -/// Plugin names (or specs) to enable, plus the optional working directory the repository-controlled guard is evaluated against. +/// Optional project paths to include when enumerating instruction discovery targets. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsEnableRequest +public sealed class InstructionsGetDiscoveryPathsRequest { - /// Plugin names or "plugin@marketplace" specs to enable. Unknown names are ignored. Non-marketplace direct installs are always enabled and cannot be toggled via this API. - [JsonPropertyName("names")] - public IList Names { get => field ??= []; set; } - - /// Working directory whose repository `enabledPlugins` overlay decides whether this mutation is repository-controlled. Hosts that serve sessions across several repositories (the SDK server) should pass the session's directory; otherwise the guard is evaluated against the server process's own working directory, which may belong to a different repository. Defaults to the server's current working directory. - [JsonPropertyName("workingDirectory")] - public string? WorkingDirectory { get; set; } -} + /// When true, omit the host's user-level instruction targets, leaving only repository targets. For multitenant deployments (mirrors `discover`'s `excludeHostInstructions`). + [JsonPropertyName("excludeHostInstructions")] + public bool? ExcludeHostInstructions { get; set; } -/// Plugin names (or specs) to disable, plus the optional working directory the repository-controlled guard is evaluated against. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsDisableRequest -{ - /// Plugin names or "plugin@marketplace" specs to disable. Unknown names are ignored. Non-marketplace direct installs cannot be disabled via this API; uninstall them instead. Plugin-owned MCP servers are stopped in active sessions immediately; other plugin contributions remain available until each session reloads plugins. - [JsonPropertyName("names")] - public IList Names { get => field ??= []; set; } + /// Explicit discovery-only instruction roots to enumerate, including configured missing directories. Relative paths resolve against the request's effective working directory when supplied. These are not filesystem grants or preferred creation targets. Null and omission are equivalent. + [JsonPropertyName("instructionDirectories")] + public IList? InstructionDirectories { get; set; } - /// Working directory whose repository `enabledPlugins` overlay decides whether this mutation is repository-controlled. Hosts that serve sessions across several repositories (the SDK server) should pass the session's directory; otherwise the guard is evaluated against the server process's own working directory, which may belong to a different repository. Defaults to the server's current working directory. - [JsonPropertyName("workingDirectory")] - public string? WorkingDirectory { get; set; } + /// Optional list of project directory paths. When omitted or empty, only the user-level targets are returned. + [JsonPropertyName("projectPaths")] + public IList? ProjectPaths { get; set; } } -/// Trusted built-in plugin directories to use for this runtime process. +/// Skill installation management result with the honoured contract, or a typed refusal. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsBuiltinSetRequest +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(SkillInstallationManagementResultOutcome), "outcome")] +[JsonDerivedType(typeof(SkillInstallationManagementResultNegotiationRefused), "negotiation-refused")] +[JsonDerivedType(typeof(SkillInstallationManagementResultInvalidRequest), "invalid-request")] +public partial class SkillInstallationManagementResult { - /// Complete replacement set of trusted built-in plugin directories. Every entry must be an absolute local filesystem path no longer than 4096 characters. - [JsonPropertyName("paths")] - public IList Paths { get => field ??= []; set; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// Registered marketplace summary. + +/// Management outcome for verified Skill inventory, planning and removal. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MarketplaceInfo +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(SkillInstallationManagementOutcomeRecoveryRequired), "recovery-required")] +[JsonDerivedType(typeof(SkillInstallationManagementOutcomeInstallPlanned), "install-planned")] +[JsonDerivedType(typeof(SkillInstallationManagementOutcomeListed), "listed")] +[JsonDerivedType(typeof(SkillInstallationManagementOutcomeRecovered), "recovered")] +[JsonDerivedType(typeof(SkillInstallationManagementOutcomeRolledBack), "rolled-back")] +[JsonDerivedType(typeof(SkillInstallationManagementOutcomeUninstallPlanned), "uninstall-planned")] +[JsonDerivedType(typeof(SkillInstallationManagementOutcomeOperation), "operation")] +[JsonDerivedType(typeof(SkillInstallationManagementOutcomeEnabledChanged), "enabled-changed")] +[JsonDerivedType(typeof(SkillInstallationManagementOutcomeRefused), "refused")] +public partial class SkillInstallationManagementOutcome { - /// Whether the managed marketplace currently resolved into the runtime marketplace registry. Set to false when the desired managed entry is retained for governance visibility after loading or reconciliation failed. - [JsonPropertyName("available")] - public bool? Available { get; set; } - - /// True when this is a default marketplace shipped with the runtime. Defaults are not removable. - [JsonPropertyName("isDefault")] - public bool? IsDefault { get; set; } - - /// Whether enterprise managed settings provide and control this marketplace entry. - [JsonPropertyName("managed")] - public bool? Managed { get; set; } - - /// Marketplace name (matches the @marketplace suffix in plugin specs). - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - /// Human-readable description of where the marketplace data is fetched from (e.g. "GitHub: owner/repo"). - [JsonPropertyName("source")] - public string Source { get; set; } = string.Empty; + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// All registered marketplaces, including built-in defaults. + +/// Already-confirmed durable work must be reconciled before new mutations or inventory. +/// The recovery-required variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MarketplaceListResult +public partial class SkillInstallationManagementOutcomeRecoveryRequired : SkillInstallationManagementOutcome { - /// Registered marketplaces. - [JsonPropertyName("marketplaces")] - public IList Marketplaces { get => field ??= []; set; } + /// + [JsonIgnore] + public override string Kind => "recovery-required"; } -/// Result of registering a new marketplace. +/// Safe verified Skill review fields. No raw credential, candidate handle or plan handle. +/// Polymorphic base type discriminated by action. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MarketplaceAddResult +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "action", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(SkillInstallationReviewInstall), "install")] +[JsonDerivedType(typeof(SkillInstallationReviewUninstall), "uninstall")] +public partial class SkillInstallationReview { - /// Final name of the marketplace as resolved from its manifest. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// The type discriminator. + [JsonPropertyName("action")] + public virtual string Action { get; set; } = string.Empty; } -/// Marketplace source and optional working directory for relative-path resolution. + +/// One reviewed Skill file. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsMarketplacesAddRequest +public sealed class SkillInstallationFileReview { - /// Marketplace source. Accepts the same forms as the CLI: "owner/repo" or "owner/repo#ref" (GitHub), an http/https/ssh URL (optionally with #ref), a git scp-style URL (user@host:path), or a local path. The marketplace's own name (from its manifest) is used as the registration key. - [JsonPropertyName("source")] - public string Source { get; set; } = string.Empty; + /// SHA-256 digest of the exact file bytes. + [JsonPropertyName("digest")] + public string Digest { get; set; } = string.Empty; - /// Working directory used to resolve relative local paths in `source`. Defaults to the server's current working directory. - [JsonPropertyName("workingDirectory")] - public string? WorkingDirectory { get; set; } + /// Whether the file is installed with executable permissions. + [JsonPropertyName("executable")] + public bool Executable { get; set; } + + /// Declared media type for the file. + [JsonPropertyName("mediaType")] + public string MediaType { get; set; } = string.Empty; + + /// Relative file path within the Skill root. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + + /// Exact reviewed file size in bytes. + [JsonPropertyName("sizeBytes")] + public long SizeBytes { get; set; } } -/// Outcome of the remove attempt, including dependent-plugin info when applicable. +/// Source identity retained from Agent Finder and the pinned GitHub descriptor. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MarketplaceRemoveResult +public sealed class SkillInstallationSource { - /// Names of installed plugins that prevented removal. Populated only when `removed=false`. - [JsonPropertyName("dependentPlugins")] - public IList? DependentPlugins { get; set; } + /// Digest of the descriptor's bundle manifest. + [JsonPropertyName("bundleDigest")] + public string BundleDigest { get; set; } = string.Empty; - /// True when the marketplace was actually removed. False when removal was skipped because the marketplace has dependent plugins and `force` was not set. - [JsonPropertyName("removed")] - public bool Removed { get; set; } + /// Agent Finder materialisation revision identifier. + [JsonPropertyName("catalogRevisionId")] + public string CatalogRevisionId { get; set; } = string.Empty; + + /// Digest of the canonical materialisation descriptor. + [JsonPropertyName("descriptorDigest")] + public string DescriptorDigest { get; set; } = string.Empty; + + /// Repository full name, for example owner/name. + [JsonPropertyName("repository")] + public string Repository { get; set; } = string.Empty; + + /// GitHub repository database identifier. + [JsonPropertyName("repositoryId")] + public string RepositoryId { get; set; } = string.Empty; + + /// Agent Finder resource identifier. + [JsonPropertyName("resourceId")] + public string ResourceId { get; set; } = string.Empty; + + /// Pinned Git commit revision. + [JsonPropertyName("revision")] + public string Revision { get; set; } = string.Empty; + + /// Root path within the pinned repository. + [JsonPropertyName("root")] + public string Root { get; set; } = string.Empty; } -/// Name of the marketplace to remove and an optional force flag. +/// A user-facing personal Skill installation location without absolute host paths. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsMarketplacesRemoveRequest +public sealed class SkillInstallationLocation { - /// When true, also uninstall every plugin sourced from this marketplace. When false (default), removal is a no-op if any plugin from this marketplace is installed and the dependent plugin names are returned in the result. - [JsonPropertyName("force")] - public bool? Force { get; set; } + /// Diagnostics-only absolute host path. Hosts must not display it by default. + [JsonPropertyName("diagnosticsAbsolutePath")] + public string? DiagnosticsAbsolutePath { get; set; } - /// Marketplace name to remove. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Safe display label, for example ~/.copilot/skills/run-checks. + [JsonPropertyName("displayLabel")] + public string DisplayLabel { get; set; } = string.Empty; + + /// Path relative to the Copilot home. + [JsonPropertyName("relativePath")] + public string RelativePath { get; set; } = string.Empty; + + /// Installation scope. Agent Finder Skills are installed in the user's personal Copilot home. + [JsonPropertyName("scope")] + public SkillInstallationScope Scope { get; set; } } -/// Plugin entry advertised by a marketplace. +/// Review for installing a verified Skill. +/// The install variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MarketplacePluginInfo +public partial class SkillInstallationReviewInstall : SkillInstallationReview { - /// Short description from the marketplace catalog, when present. + /// + [JsonIgnore] + public override string Action => "install"; + + /// Catalogue identity retained from the bound candidate before consent. + [JsonPropertyName("catalogue")] + public required InstallationCatalogueIdentity Catalogue { get; set; } + + /// Skill description from SKILL.md when present. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("description")] public string? Description { get; set; } - /// Plugin name as listed in the marketplace catalog. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; -} + /// + /// Complete verified SKILL.md content. Planning refuses with review-too-large + /// when this exceeds 262144 UTF-8 bytes; it is never truncated. + /// + [JsonPropertyName("entrypointContent")] + public required string EntrypointContent { get; set; } -/// Plugins advertised by the marketplace. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MarketplaceBrowseResult -{ - /// Plugins advertised by the marketplace. - [JsonPropertyName("plugins")] - public IList Plugins { get => field ??= []; set; } -} + /// Relative path of the verified Skill entrypoint. + [JsonPropertyName("entrypointPath")] + public required string EntrypointPath { get; set; } -/// Name of the marketplace whose plugin catalog to fetch. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsMarketplacesBrowseRequest -{ - /// Marketplace name to browse. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; -} + /// Reviewed files and digests. + [JsonPropertyName("files")] + public required IList Files { get; set; } -/// Per-marketplace refresh result, including marketplace name, success flag, and optional failure error. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MarketplaceRefreshEntry -{ - /// Error message (failure only). - [JsonPropertyName("error")] - public string? Error { get; set; } + /// Installing never grants immediate use; the Skill is written disabled. + [JsonPropertyName("installsDisabled")] + public required bool InstallsDisabled { get; set; } - /// Marketplace name that was refreshed. + /// Skill invocation name from SKILL.md. [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + public required string Name { get; set; } - /// Whether the refresh succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } -} + /// Exact verified source identity. + [JsonPropertyName("source")] + public required SkillInstallationSource Source { get; set; } -/// Result of refreshing one or more marketplace catalogs. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MarketplaceRefreshResult -{ - /// Per-marketplace refresh results in deterministic order. - [JsonPropertyName("results")] - public IList Results { get => field ??= []; set; } -} + /// Exact user-scope target location without an absolute host path. + [JsonPropertyName("target")] + public required SkillInstallationLocation Target { get; set; } -/// RPC data type for PluginsMarketplacesRefresh operations. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsMarketplacesRefreshRequest -{ - /// Marketplace name to refresh. When omitted, every registered marketplace is refreshed. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// Total reviewed payload size in bytes. + [JsonPropertyName("totalBytes")] + public required long TotalBytes { get; set; } } -/// Agent metadata, including identifiers, display details, source, tools, model, models, reasoning effort, MCP servers, skills, and file path. +/// Durable verified Skill ownership summary. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AgentInfo +public sealed class SkillInstallationSummary { - /// Description of the agent's purpose. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; - - /// Whether model-driven invocation is disabled for this agent. - [JsonPropertyName("disableModelInvocation")] - public bool? DisableModelInvocation { get; set; } - - /// Human-readable display name. - [JsonPropertyName("displayName")] - public string DisplayName { get; set; } = string.Empty; - - /// Stable identifier for selection. For most agents this is the same as `name`; for plugin/builtin agents it may differ. Always populated; defaults to `name` when no distinct id was assigned. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; - - /// MCP server configurations attached to this agent, keyed by server name. Server config shape mirrors the MCP `mcpServers` schema. - [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] - [JsonPropertyName("mcpServers")] - public IDictionary? McpServers { get; set; } + /// Catalogue identity retained at install time. + [JsonPropertyName("catalogue")] + public InstallationCatalogueIdentity Catalogue { get => field ??= new(); set; } - /// Authored preferred model id for this agent. Runtime model selection may choose a different model; omitted means no authored preference. - [JsonPropertyName("model")] - public string? Model { get; set; } + /// Persisted enablement requested for this installation. + [JsonPropertyName("configuredEnabled")] + public bool ConfiguredEnabled { get; set; } - /// Whether authored models are preferences or required constraints. - [JsonPropertyName("modelPolicy")] - public AgentModelPolicy? ModelPolicy { get; set; } + /// Exact durable installation receipt identity. + [JsonPropertyName("installationId")] + public string InstallationId { get; set; } = string.Empty; - /// Authored preferred model ids for this agent, in priority order. Runtime model selection chooses the first available model; omitted means no authored preference. - [JsonPropertyName("models")] - public IList? Models { get; set; } + /// ISO 8601 wall-clock installation time. + [JsonPropertyName("installedAt")] + public string InstalledAt { get; set; } = string.Empty; - /// Name of the agent. Use `id` as the stable selection identifier. + /// Skill invocation name. [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; - /// Absolute local file path of the agent definition. Only set for file-based agents loaded from disk; remote agents do not have a path. - [JsonPropertyName("path")] - public string? Path { get; set; } - - /// Authored base prompt for the agent. Runtime prompt assembly may add dynamic context at invocation time. Omitted from `session.agent.list` unless `includePrompt` is true. - [JsonPropertyName("prompt")] - public string? Prompt { get; set; } + /// Operation that installed this Skill. + [JsonPropertyName("operationId")] + public string OperationId { get; set; } = string.Empty; - /// Authored reasoning effort for this agent. Applied on selection to models that support it; omitted means no authored preference. - [JsonPropertyName("reasoningEffort")] - public string? ReasoningEffort { get; set; } + /// Ownership state observed from files and receipts. + [JsonPropertyName("ownershipState")] + public SkillInstallationOwnershipState OwnershipState { get; set; } - /// Skill names preloaded into this agent's context. Omitted means none. - [JsonPropertyName("skills")] - public IList? Skills { get; set; } + /// Bound-session load observation. + [JsonPropertyName("sessionState")] + public SkillInstallationSessionState SessionState { get; set; } - /// Where the agent definition was loaded from. + /// Exact retained verified source identity. [JsonPropertyName("source")] - public AgentInfoSource? Source { get; set; } - - /// Allowed tool names for this agent. Empty array means none; omitted means inherit defaults. - [JsonPropertyName("tools")] - public IList? Tools { get; set; } + public SkillInstallationSource Source { get => field ??= new(); set; } - /// Whether the agent can be selected directly by the user. Agents marked `false` are subagent-only. - [JsonPropertyName("userInvocable")] - public bool? UserInvocable { get; set; } + /// User-facing installation location without an absolute host path. + [JsonPropertyName("target")] + public SkillInstallationLocation Target { get => field ??= new(); set; } } -/// Agents discovered across user, project, plugin, and remote sources. +/// Review for uninstalling an owned verified Skill. +/// The uninstall variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ServerAgentList +public partial class SkillInstallationReviewUninstall : SkillInstallationReview { - /// All discovered agents across all sources. - [JsonPropertyName("agents")] - public IList Agents { get => field ??= []; set; } -} + /// + [JsonIgnore] + public override string Action => "uninstall"; -/// Optional project paths to include in agent discovery. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AgentsDiscoverRequest -{ - /// When true, omit the host's agents (the user-level agent directory and all plugin agents), leaving only project and remote agents. For multitenant deployments. - [JsonPropertyName("excludeHostAgents")] - public bool? ExcludeHostAgents { get; set; } + /// Catalogue identity retained at install time. + [JsonPropertyName("catalogue")] + public required InstallationCatalogueIdentity Catalogue { get; set; } - /// Optional list of project directory paths to scan for project-scoped agents. When omitted or empty, only user/plugin/remote-independent agents are returned (no project scan). - [JsonPropertyName("projectPaths")] - public IList? ProjectPaths { get; set; } + /// Files recorded by the installation receipt. + [JsonPropertyName("files")] + public required IList Files { get; set; } + + /// Whether current files differ from the receipt. Apply refuses drift. + [JsonPropertyName("filesModified")] + public required bool FilesModified { get; set; } + + /// Owned installation being removed. + [JsonPropertyName("installation")] + public required SkillInstallationSummary Installation { get; set; } + + /// Total receipt-owned payload size in bytes. + [JsonPropertyName("totalBytes")] + public required long TotalBytes { get; set; } } -/// Canonical directory where custom agents can be discovered or created, with scope, preference, and optional project path. +/// A computed Skill install plan. Nothing has been applied. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AgentDiscoveryPath +public sealed class SkillInstallPlan { - /// Absolute path of the search/create directory (may not exist on disk yet). - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Original wall-clock expiry as an ISO 8601 timestamp. + [JsonPropertyName("expiresAt")] + public string ExpiresAt { get; set; } = string.Empty; - /// Whether this is the canonical directory to create a new agent in its tier. At most one entry per tier is preferred. - [JsonPropertyName("preferredForCreation")] - public bool PreferredForCreation { get; set; } + /// Original operation identifier returned before confirmation. + [JsonPropertyName("operationId")] + public string OperationId { get; set; } = string.Empty; - /// The input project path this directory was derived from (only for project scope). - [JsonPropertyName("projectPath")] - public string? ProjectPath { get; set; } + /// One-use plan handle, bound to the original candidate authority. + [JsonPropertyName("planHandle")] + public string PlanHandle { get; set; } = string.Empty; - /// Which tier this directory belongs to. - [JsonPropertyName("scope")] - public AgentDiscoveryPathScope Scope { get; set; } + /// Safe review to present before applying the plan. + [JsonPropertyName("review")] + public SkillInstallationReview Review { get => field ??= new(); set; } } -/// Canonical locations where custom agents can be created so the runtime will recognize them. +/// A verified Skill installation plan was prepared. +/// The install-planned variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AgentDiscoveryPathList +public partial class SkillInstallationManagementOutcomeInstallPlanned : SkillInstallationManagementOutcome { - /// Canonical agent create/discovery directories, in priority order. - [JsonPropertyName("paths")] - public IList Paths { get => field ??= []; set; } + /// + [JsonIgnore] + public override string Kind => "install-planned"; + + /// Prepared install plan. + [JsonPropertyName("plan")] + public required SkillInstallPlan Plan { get; set; } } -/// Optional project paths to include when enumerating agent discovery directories. +/// Owned Skill installations were listed. +/// The listed variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AgentsGetDiscoveryPathsRequest +public partial class SkillInstallationManagementOutcomeListed : SkillInstallationManagementOutcome { - /// When true, omit the host's user-level agent directory, leaving only project directories. For multitenant deployments (mirrors `discover`'s `excludeHostAgents`). - [JsonPropertyName("excludeHostAgents")] - public bool? ExcludeHostAgents { get; set; } + /// + [JsonIgnore] + public override string Kind => "listed"; - /// Optional list of project directory paths. When omitted or empty, only the user-level directory is returned. - [JsonPropertyName("projectPaths")] - public IList? ProjectPaths { get; set; } + /// Owned Skill installation summaries. + [JsonPropertyName("installations")] + public required IList Installations { get; set; } } -/// The agents this runtime ships, named so a consumer can tell them apart from authored ones. +/// Recovery completed and inventory was inspected. +/// The recovered variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AgentsGetBuiltinsResult +public partial class SkillInstallationManagementOutcomeRecovered : SkillInstallationManagementOutcome { - /// The subset of `names` a user is allowed to turn off. A shipped agent outside this list is always active and a client should not offer a toggle for it. - [JsonPropertyName("disableableNames")] - public IList DisableableNames { get => field ??= []; set; } - - /// Every agent name this runtime ships. - [JsonPropertyName("names")] - public IList Names { get => field ??= []; set; } + /// + [JsonIgnore] + public override string Kind => "recovered"; - /// The subset of `names` defined by a shipped YAML definition. The remainder are special-cased in code and have no definition to load. - [JsonPropertyName("yamlBasedNames")] - public IList YamlBasedNames { get => field ??= []; set; } + /// Owned Skill installation summaries after recovery. + [JsonPropertyName("installations")] + public required IList Installations { get; set; } } -/// A shipped agent, named and described. +/// Interrupted work was safely compensated and the pending marker was cleared. +/// The rolled-back variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class BuiltinAgentSummary +public partial class SkillInstallationManagementOutcomeRolledBack : SkillInstallationManagementOutcome { - /// One-line description of what the agent does. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "rolled-back"; - /// The agent name, as it appears in `getBuiltins`. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; -} + /// Original operation identity. + [JsonPropertyName("operation_id")] + public required string OperationId { get; set; } -/// The shipped agents available under the requested flags. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AgentsGetAvailableBuiltinsResult -{ - /// Available shipped agents, in the runtime's own order. - [JsonPropertyName("agents")] - public IList Agents { get => field ??= []; set; } + /// Cause of the compensation. + [JsonPropertyName("reason")] + public required SkillInstallationFailureReason Reason { get; set; } } -/// The feature flags to evaluate shipped agents against. +/// A computed Skill uninstall plan. Nothing has been removed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AgentsGetAvailableBuiltinsRequest +public sealed class SkillUninstallPlan { - /// The surface asking, which gates agents that only apply to one client. Omit or pass null to apply no client filter. - [JsonPropertyName("context")] - public string? Context { get; set; } + /// Original wall-clock expiry as an ISO 8601 timestamp. + [JsonPropertyName("expiresAt")] + public string ExpiresAt { get; set; } = string.Empty; - /// Feature flag values keyed by name, evaluated with the runtime's truthiness rules. Omit or pass null for no flags. - [JsonPropertyName("featureFlags")] - public IDictionary? FeatureFlags { get; set; } + /// Owned Skill installation being removed. + [JsonPropertyName("installation")] + public SkillInstallationSummary Installation { get => field ??= new(); set; } - /// Flag overrides keyed by name. A null entry uses the corresponding base flag; false explicitly disables it. Omit or pass null for no overrides. - [JsonPropertyName("overrides")] - public IDictionary? Overrides { get; set; } + /// Original removal operation identifier returned before confirmation. + [JsonPropertyName("operationId")] + public string OperationId { get; set; } = string.Empty; + + /// One-use uninstall plan handle. + [JsonPropertyName("planHandle")] + public string PlanHandle { get; set; } = string.Empty; + + /// Safe review to present before applying removal. + [JsonPropertyName("review")] + public SkillInstallationReview Review { get => field ??= new(); set; } } -/// One shipped agent's definition. +/// An owned Skill uninstall plan was prepared. +/// The uninstall-planned variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AgentsGetBuiltinDefinitionResult +public partial class SkillInstallationManagementOutcomeUninstallPlanned : SkillInstallationManagementOutcome { - /// The agent's definition, serialized as JSON. It carries the authored keys plus the runtime's projected `__nativeCustomAgent` view of the same agent. It is a string rather than an object because the runtime parses it with the agent schema's tolerant shape, which accepts keys this contract does not name. - [JsonPropertyName("definitionJson")] - public string DefinitionJson { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "uninstall-planned"; + + /// Prepared uninstall plan. + [JsonPropertyName("plan")] + public required SkillUninstallPlan Plan { get; set; } } -/// The shipped agent whose definition to load. +/// Status snapshot from the original connection, independent of new-work account availability. +/// Polymorphic base type discriminated by phase. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AgentsGetBuiltinDefinitionRequest +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "phase", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(SkillInstallationOperationStatusPreparing), "preparing")] +[JsonDerivedType(typeof(SkillInstallationOperationStatusPrepared), "prepared")] +[JsonDerivedType(typeof(SkillInstallationOperationStatusAwaitingConfirmation), "awaiting-confirmation")] +[JsonDerivedType(typeof(SkillInstallationOperationStatusRevalidating), "revalidating")] +[JsonDerivedType(typeof(SkillInstallationOperationStatusApplying), "applying")] +[JsonDerivedType(typeof(SkillInstallationOperationStatusCompleted), "completed")] +public partial class SkillInstallationOperationStatus { - /// The agent name, which must be one of `getBuiltins`'s `yamlBasedNames`. A name outside that list is special-cased in code and has no definition, and is reported as an error rather than as an empty definition. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// The type discriminator. + [JsonPropertyName("phase")] + public virtual string Phase { get; set; } = string.Empty; } -/// One shipped agent, projected for a listing. + +/// Original operation progress discriminator. +/// The preparing variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AgentsGetBuiltinListingDefinitionResult +public partial class SkillInstallationOperationStatusPreparing : SkillInstallationOperationStatus { - /// The agent projected as a custom agent, serialized as JSON. It is a string rather than an object for the same reason as `getBuiltinDefinition`: the runtime parses the underlying definition with the agent schema's tolerant shape, which accepts keys this contract does not name. - [JsonPropertyName("definitionJson")] - public string DefinitionJson { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Phase => "preparing"; + + /// Whether cancellation has been requested. + [JsonPropertyName("cancellationRequested")] + public required bool CancellationRequested { get; set; } + + /// Original runtime-issued operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } -/// The shipped agent whose listing entry to load. +/// Original operation progress discriminator. +/// The prepared variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AgentsGetBuiltinListingDefinitionRequest +public partial class SkillInstallationOperationStatusPrepared : SkillInstallationOperationStatus { - /// The agent name, taken from `getAvailableBuiltins`. Unlike `getBuiltinDefinition`, the agent that `getBuiltins` reports as special-cased rather than YAML-based is answered here too, from its in-code definition. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Phase => "prepared"; + + /// Whether cancellation has been requested. + [JsonPropertyName("cancellationRequested")] + public required bool CancellationRequested { get; set; } + + /// Original runtime-issued operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } -/// The model to switch to, and the warning to show when the agent's preference could not be met. +/// Original operation progress discriminator. +/// The awaiting-confirmation variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AgentsCustomAgentInitialModelDecisionResult +public partial class SkillInstallationOperationStatusAwaitingConfirmation : SkillInstallationOperationStatus { - /// The reasoning effort attached to the selected model preference. Absent when that preference does not specify an effort. - [JsonPropertyName("reasoningEffort")] - public string? ReasoningEffort { get; set; } + /// + [JsonIgnore] + public override string Phase => "awaiting-confirmation"; - /// The first available model that matches the agent's preferences. Absent when none of the requested models is available. - [JsonPropertyName("targetModel")] - public string? TargetModel { get; set; } + /// Whether cancellation has been requested. + [JsonPropertyName("cancellationRequested")] + public required bool CancellationRequested { get; set; } - /// What to tell the user about an unmet preference. Absent when the preference was met. A warning with no `targetModel` means the agent's models are all unavailable. - [JsonPropertyName("warning")] - public string? Warning { get; set; } + /// Original runtime-issued operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } -/// The models a custom agent asks for, and the models actually available. +/// Original operation progress discriminator. +/// The revalidating variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AgentsCustomAgentInitialModelDecisionParams +public partial class SkillInstallationOperationStatusRevalidating : SkillInstallationOperationStatus { - /// The agent's declared `model:` entry, serialized. A single name or an ordered list of acceptable names. - [JsonPropertyName("agentModelsJson")] - public string AgentModelsJson { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Phase => "revalidating"; - /// The models available to this session, serialized in the shape the model list carries. - [JsonPropertyName("availableModelsJson")] - public string AvailableModelsJson { get; set; } = string.Empty; + /// Whether cancellation has been requested. + [JsonPropertyName("cancellationRequested")] + public required bool CancellationRequested { get; set; } + + /// Original runtime-issued operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } -/// Loaded instruction source for a session, including path, content, category, location, applicability, and optional description. +/// Original operation progress discriminator. +/// The applying variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class InstructionSource +public partial class SkillInstallationOperationStatusApplying : SkillInstallationOperationStatus { - /// Glob pattern(s) from frontmatter — when set, this instruction applies only to matching files. - [JsonPropertyName("applyTo")] - public IList? ApplyTo { get; set; } + /// + [JsonIgnore] + public override string Phase => "applying"; - /// Raw content of the instruction file. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// Whether cancellation has been requested; already-started effects require recovery. + [JsonPropertyName("cancellationRequested")] + public required bool CancellationRequested { get; set; } - /// When true, this source starts disabled and must be toggled on by the user. - [JsonPropertyName("defaultDisabled")] - public bool? DefaultDisabled { get; set; } + /// Original runtime-issued operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } +} - /// Short description (body after frontmatter) for use in instruction tables. - [JsonPropertyName("description")] - public string? Description { get; set; } +/// Terminal verified Skill mutation result. +/// Polymorphic base type discriminated by kind. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(SkillInstallationOutcomeInstalled), "installed")] +[JsonDerivedType(typeof(SkillInstallationOutcomeUninstalled), "uninstalled")] +[JsonDerivedType(typeof(SkillInstallationOutcomeRolledBack), "rolled-back")] +[JsonDerivedType(typeof(SkillInstallationOutcomeRecoveryRequired), "recovery-required")] +[JsonDerivedType(typeof(SkillInstallationOutcomeDeclined), "declined")] +[JsonDerivedType(typeof(SkillInstallationOutcomeCancelled), "cancelled")] +[JsonDerivedType(typeof(SkillInstallationOutcomeRefused), "refused")] +public partial class SkillInstallationOutcome +{ + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// Unique identifier for this source (used for toggling). - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; - /// Human-readable label. - [JsonPropertyName("label")] - public string Label { get; set; } = string.Empty; +/// The verified Skill was installed disabled. +/// The installed variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class SkillInstallationOutcomeInstalled : SkillInstallationOutcome +{ + /// + [JsonIgnore] + public override string Kind => "installed"; - /// Where this source lives — used for UI grouping. - [JsonPropertyName("location")] - public InstructionSourceLocation Location { get; set; } + /// Durable installed Skill summary. + [JsonPropertyName("installation")] + public required SkillInstallationSummary Installation { get; set; } +} - /// The project path this source was discovered from. Only set by sessionless discovery for repository, working-directory, and project-scoped plugin sources, where it disambiguates sources across multiple workspace roots. The session-scoped getSources leaves it unset. - [JsonPropertyName("projectPath")] - public string? ProjectPath { get; set; } +/// The owned Skill was removed. +/// The uninstalled variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class SkillInstallationOutcomeUninstalled : SkillInstallationOutcome +{ + /// + [JsonIgnore] + public override string Kind => "uninstalled"; - /// File path relative to repo or absolute for home. - [JsonPropertyName("sourcePath")] - public string SourcePath { get; set; } = string.Empty; + /// Removed installation identity. + [JsonPropertyName("installationId")] + public required string InstallationId { get; set; } - /// Category of instruction source — used for merge logic. - [JsonPropertyName("type")] - public InstructionSourceType Type { get; set; } + /// Original removal operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } -/// Instruction sources discovered across user, repository, and plugin sources. +/// The durable transaction was aborted or fully compensated. +/// The rolled-back variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ServerInstructionSourceList +public partial class SkillInstallationOutcomeRolledBack : SkillInstallationOutcome { - /// All discovered instruction sources. - [JsonPropertyName("sources")] - public IList Sources { get => field ??= []; set; } + /// + [JsonIgnore] + public override string Kind => "rolled-back"; + + /// Original operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } + + /// Cause of the fully aborted or compensated operation. + [JsonPropertyName("reason")] + public required SkillInstallationFailureReason Reason { get; set; } } -/// Optional project paths to include in instruction discovery. +/// A write may have completed. Recover and inspect durable state before retrying. +/// The recovery-required variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class InstructionsDiscoverRequest +public partial class SkillInstallationOutcomeRecoveryRequired : SkillInstallationOutcome { - /// When true, omit the host's instruction sources (user/home-level files and plugin rules), leaving only repository and working-directory sources. For multitenant deployments. - [JsonPropertyName("excludeHostInstructions")] - public bool? ExcludeHostInstructions { get; set; } + /// + [JsonIgnore] + public override string Kind => "recovery-required"; - /// Optional list of project directory paths to scan for repository/working-directory instruction sources. When omitted or empty, only user-level and plugin instruction sources are returned (no project scan). - [JsonPropertyName("projectPaths")] - public IList? ProjectPaths { get; set; } + /// Operation whose durable result must be recovered and inspected. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } -/// Canonical file or directory where custom instructions can be discovered or created, with location, kind, preference, and project path. +/// The user declined the confirmation request. +/// The declined variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class InstructionDiscoveryPath +public partial class SkillInstallationOutcomeDeclined : SkillInstallationOutcome { - /// Whether the target is a single file or a directory of instruction files. - [JsonPropertyName("kind")] - public InstructionDiscoveryPathKind Kind { get; set; } - - /// Which tier this target belongs to. - [JsonPropertyName("location")] - public InstructionDiscoveryPathLocation Location { get; set; } - - /// Absolute path of the file or directory (may not exist on disk yet). - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; - - /// Whether this is the canonical target to create new instructions in its tier. At most one entry per tier is preferred. - [JsonPropertyName("preferredForCreation")] - public bool PreferredForCreation { get; set; } + /// + [JsonIgnore] + public override string Kind => "declined"; - /// The input project path this target was derived from (only for repository targets). - [JsonPropertyName("projectPath")] - public string? ProjectPath { get; set; } + /// Original operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } -/// Canonical files and directories where custom instructions can be created so the runtime will recognize them. +/// The operation was cancelled before a terminal mutation. +/// The cancelled variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class InstructionDiscoveryPathList +public partial class SkillInstallationOutcomeCancelled : SkillInstallationOutcome { - /// Canonical instruction create/discovery files and directories, in priority order. - [JsonPropertyName("paths")] - public IList Paths { get => field ??= []; set; } + /// + [JsonIgnore] + public override string Kind => "cancelled"; + + /// Original operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } -/// Optional project paths to include when enumerating instruction discovery targets. +/// The operation was refused without applying changes. +/// The refused variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class InstructionsGetDiscoveryPathsRequest +public partial class SkillInstallationOutcomeRefused : SkillInstallationOutcome { - /// When true, omit the host's user-level instruction targets, leaving only repository targets. For multitenant deployments (mirrors `discover`'s `excludeHostInstructions`). - [JsonPropertyName("excludeHostInstructions")] - public bool? ExcludeHostInstructions { get; set; } + /// + [JsonIgnore] + public override string Kind => "refused"; - /// Optional list of project directory paths. When omitted or empty, only the user-level targets are returned. - [JsonPropertyName("projectPaths")] - public IList? ProjectPaths { get; set; } + /// Present once an operation has been allocated. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("operationId")] + public string? OperationId { get; set; } + + /// Bounded refusal reason. + [JsonPropertyName("reason")] + public required SkillInstallationFailureReason Reason { get; set; } } -/// Installed plugin record from global state, with marketplace, version, install time, enabled state, cache path, and source. +/// Original operation progress discriminator. +/// The completed variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class InstalledPlugin +public partial class SkillInstallationOperationStatusCompleted : SkillInstallationOperationStatus { - /// Path where the plugin is cached locally. - [JsonPropertyName("cache_path")] - public string? CachePath { get; set; } + /// + [JsonIgnore] + public override string Phase => "completed"; - /// Whether the plugin is currently enabled. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } + /// Whether cancellation was requested before the terminal result. + [JsonPropertyName("cancellationRequested")] + public required bool CancellationRequested { get; set; } - /// Installation timestamp. - [JsonPropertyName("installed_at")] - public string InstalledAt { get; set; } = string.Empty; + /// Original runtime-issued operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } - /// Absolute path of the marketplace directory a live plugin was resolved from. Present only on live, never-persisted records — those synthesized at session start for a directory/local marketplace, whose cache_path points at the real plugin directory on disk rather than a copy under the installed-plugins cache. Its presence is what marks a record as live, and no record carrying it is ever written to the persisted installedPlugins key. - [JsonPropertyName("installed_from")] - public string? InstalledFrom { get; set; } + /// Immutable terminal receipt. + [JsonPropertyName("outcome")] + public required SkillInstallationOutcome Outcome { get; set; } +} - /// Marketplace the plugin came from (empty string for direct repo installs). - [JsonPropertyName("marketplace")] - public string Marketplace { get; set; } = string.Empty; +/// Original-connection operation snapshot. +/// The operation variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class SkillInstallationManagementOutcomeOperation : SkillInstallationManagementOutcome +{ + /// + [JsonIgnore] + public override string Kind => "operation"; - /// Plugin name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Operation status. + [JsonPropertyName("operation")] + public required SkillInstallationOperationStatus Operation { get; set; } +} - /// Source for direct repo installs (when marketplace is empty). - [JsonPropertyName("source")] - public JsonElement? Source { get; set; } +/// Enablement changed and the selected session was reconciled. +/// The enabled-changed variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class SkillInstallationManagementOutcomeEnabledChanged : SkillInstallationManagementOutcome +{ + /// + [JsonIgnore] + public override string Kind => "enabled-changed"; - /// Per-plugin source fingerprint (a SHA-256 hash of the plugin's catalog source spec plus its resolved source subtree — NOT a Git commit SHA) captured at marketplace install/update time. Auto-update compares it against the freshly recomputed fingerprint to detect a content change that does not bump the version. Absent for pre-existing installs and for direct (non-marketplace) installs. - [JsonPropertyName("source_sha")] - public string? SourceSha { get; set; } + /// Safe reload or reconciliation diagnostics. + [JsonPropertyName("diagnostics")] + public required IList Diagnostics { get; set; } - /// Version installed (if available). - [JsonPropertyName("version")] - public string? Version { get; set; } + /// Updated installation summary. + [JsonPropertyName("installation")] + public required SkillInstallationSummary Installation { get; set; } } -/// An account the host has signed in to, identified by the server it lives on and the login it uses there. The same person can appear more than once when they use both github.com and an Enterprise server. +/// The management request was refused. +/// The refused variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class LoggedInUser +public partial class SkillInstallationManagementOutcomeRefused : SkillInstallationManagementOutcome { - /// Source account this account was derived from, when one was recorded. - [JsonPropertyName("derivedFrom")] - public string? DerivedFrom { get; set; } - - /// Host the account belongs to, such as `github.com` or an Enterprise server. - [JsonPropertyName("host")] - public string Host { get; set; } = string.Empty; - - /// Account kind, when the host recorded one. Consumers must tolerate new strings. - [JsonPropertyName("kind")] - public string? Kind { get; set; } + /// + [JsonIgnore] + public override string Kind => "refused"; - /// Account login on that host. - [JsonPropertyName("login")] - public string Login { get; set; } = string.Empty; + /// Bounded refusal reason. + [JsonPropertyName("reason")] + public required SkillInstallationFailureReason Reason { get; set; } } -/// The host's machine-wide state. Every field is optional because a fresh install has recorded nothing yet, so a reader must treat an absent field as `not yet`, never as a negative answer. Stored credentials are deliberately absent from this shape. +/// The outcome variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GlobalStateLoadResult +public partial class SkillInstallationManagementResultOutcome : SkillInstallationManagementResult { - /// Whether the user has answered the prompt suggesting they install the desktop app. - [JsonPropertyName("appInstallNudgeResponded")] - public bool? AppInstallNudgeResponded { get; set; } + /// + [JsonIgnore] + public override string Kind => "outcome"; - /// Whether the app tip has been shown. - [JsonPropertyName("appTipShown")] - public bool? AppTipShown { get; set; } + /// Capabilities honoured for this request. + [JsonPropertyName("negotiated")] + public required CatalogNegotiatedContract Negotiated { get; set; } - /// Terminals the user has already been asked to set up, so the host does not ask twice. - [JsonPropertyName("askedSetupTerminals")] - public IList? AskedSetupTerminals { get; set; } + /// Observed management outcome. + [JsonPropertyName("outcome")] + public required SkillInstallationManagementOutcome Outcome { get; set; } +} - /// When the Auto-feedback hint was last shown, as an ISO 8601 timestamp. It enforces the once-per-day cap for non-staff users across restarts. - [JsonPropertyName("autoFeedbackLastPromptedAt")] - public string? AutoFeedbackLastPromptedAt { get; set; } +/// The caller's protocol version or required capabilities cannot be honoured. Returned instead of a partial or ambiguous success. +/// The negotiation-refused variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class SkillInstallationManagementResultNegotiationRefused : SkillInstallationManagementResult +{ + /// + [JsonIgnore] + public override string Kind => "negotiation-refused"; - /// When the host first ran on this machine. - [JsonPropertyName("firstLaunchAt")] - public string? FirstLaunchAt { get; set; } + /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } - /// Plugins installed on this machine. - [JsonPropertyName("installedPlugins")] - public IList? InstalledPlugins { get; set; } + /// Lowest caller protocol version this runtime will serve. + [JsonPropertyName("minimumSupportedProtocolVersion")] + public required long MinimumSupportedProtocolVersion { get; set; } - /// Account used for the most recent sign-in. - [JsonPropertyName("lastLoggedInUser")] - public LoggedInUser? LastLoggedInUser { get; set; } + /// Whether the version or the capability set was the problem. + [JsonPropertyName("reason")] + public required CatalogNegotiationRefusedReason Reason { get; set; } - /// Every account the host has signed in to on this machine. - [JsonPropertyName("loggedInUsers")] - public IList? LoggedInUsers { get; set; } + /// Protocol version of the runtime that refused the request. + [JsonPropertyName("runtimeProtocolVersion")] + public required long RuntimeProtocolVersion { get; set; } - /// Whether the one-off cleanup of stored reasoning summaries has run. - [JsonPropertyName("reasoningSummariesCleanupDone")] - public bool? ReasoningSummariesCleanupDone { get; set; } + /// Capabilities this runtime can safely advertise to this caller. The complete five-capability protocol-3 legacy set is always present; every capability added after that baseline appears only when the caller required it, so an older closed-enum decoder can still consume a refusal. This list does not imply that every deployment has enabled every operation. + [JsonPropertyName("supportedCapabilities")] + public required IList SupportedCapabilities { get; set; } - /// Models the user selected recently, most recent first. - [JsonPropertyName("recentModelIds")] - public IList? RecentModelIds { get; set; } + /// The subset of the caller's bounded extensible capability identifiers this runtime cannot honour. + [JsonPropertyName("unsupportedCapabilities")] + public required IList UnsupportedCapabilities { get; set; } +} - /// Whether the user declined to trust the sandbox credential proxy CA. - [JsonPropertyName("sandboxCredentialProxyCaDeclined")] - public bool? SandboxCredentialProxyCaDeclined { get; set; } +/// The request was rejected because a bounded field fell outside its permitted range or a required field was unusable. Pagination may also be rejected by the authority after a continuation request; repeat the search without page. +/// The invalid-request variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class SkillInstallationManagementResultInvalidRequest : SkillInstallationManagementResult +{ + /// + [JsonIgnore] + public override string Kind => "invalid-request"; - /// Whether the sandbox onboarding has been shown. - [JsonPropertyName("sandboxOnboardingShown")] - public bool? SandboxOnboardingShown { get; set; } + /// Which request field was rejected. + [JsonPropertyName("field")] + public required CatalogInvalidRequestField Field { get; set; } - /// Whether the user is a GitHub or Microsoft staff member, which unlocks internal-only behavior. - [JsonPropertyName("staff")] - public bool? Staff { get; set; } + /// Human-readable explanation, safe to surface. Never echoes the offending value, nor a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } +} - /// Whether the user was recognized as GitHub staff. - [JsonPropertyName("staffGithub")] - public bool? StaffGitHub { get; set; } +/// Side-effect-free planning of one verified Agent Finder Skill candidate. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SkillPlanInstallRequest +{ + /// Fresh single-use AI skill candidate handle returned by a bound catalogue search. + [JsonPropertyName("candidateHandle")] + public string CandidateHandle { get; set; } = string.Empty; - /// When the staff-only log level migration last ran. - [JsonPropertyName("staffLogLevelMigrationAt")] - public string? StaffLogLevelMigrationAt { get; set; } + /// Required authenticated bound catalogue and Skill installation capabilities. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); set; } - /// Whether the user was recognized as Microsoft staff. - [JsonPropertyName("staffMicrosoft")] - public bool? StaffMicrosoft { get; set; } + /// Existing local session attached to this connection. + [JsonPropertyName("policySessionId")] + public string PolicySessionId { get; set; } = string.Empty; +} - /// When the staff-only model reset last ran. - [JsonPropertyName("staffModelResetAt")] - public string? StaffModelResetAt { get; set; } +/// Skill installation result with the honoured contract, or a typed request/negotiation refusal. +/// Polymorphic base type discriminated by kind. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(SkillInstallationResultOutcome), "outcome")] +[JsonDerivedType(typeof(SkillInstallationResultNegotiationRefused), "negotiation-refused")] +[JsonDerivedType(typeof(SkillInstallationResultInvalidRequest), "invalid-request")] +public partial class SkillInstallationResult +{ + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// When the staff-only update channel migration last ran. - [JsonPropertyName("staffUpdateChannelMigrationAt")] - public string? StaffUpdateChannelMigrationAt { get; set; } - /// Folders where the user declined the init prompt, so it stays hidden there. - [JsonPropertyName("suppressInitFolders")] - public IList? SuppressInitFolders { get; set; } +/// The outcome variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class SkillInstallationResultOutcome : SkillInstallationResult +{ + /// + [JsonIgnore] + public override string Kind => "outcome"; - /// Folders the user has marked as trusted. - [JsonPropertyName("trustedFolders")] - public IList? TrustedFolders { get; set; } + /// Capabilities honoured for this request. + [JsonPropertyName("negotiated")] + public required CatalogNegotiatedContract Negotiated { get; set; } + + /// Terminal operation outcome. + [JsonPropertyName("outcome")] + public required SkillInstallationOutcome Outcome { get; set; } } -/// Selects the configuration directory whose machine-wide state to read. +/// The caller's protocol version or required capabilities cannot be honoured. Returned instead of a partial or ambiguous success. +/// The negotiation-refused variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GlobalStateLoadForConfigDirRequest +public partial class SkillInstallationResultNegotiationRefused : SkillInstallationResult { - /// Copilot configuration directory to read the state document from, taking precedence over the server's own `COPILOT_HOME` and default home. Omit it, or pass an empty string, to read the directory the server resolved for itself. - [JsonPropertyName("configDir")] - public string? ConfigDir { get; set; } + /// + [JsonIgnore] + public override string Kind => "negotiation-refused"; + + /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } + + /// Lowest caller protocol version this runtime will serve. + [JsonPropertyName("minimumSupportedProtocolVersion")] + public required long MinimumSupportedProtocolVersion { get; set; } + + /// Whether the version or the capability set was the problem. + [JsonPropertyName("reason")] + public required CatalogNegotiationRefusedReason Reason { get; set; } + + /// Protocol version of the runtime that refused the request. + [JsonPropertyName("runtimeProtocolVersion")] + public required long RuntimeProtocolVersion { get; set; } + + /// Capabilities this runtime can safely advertise to this caller. The complete five-capability protocol-3 legacy set is always present; every capability added after that baseline appears only when the caller required it, so an older closed-enum decoder can still consume a refusal. This list does not imply that every deployment has enabled every operation. + [JsonPropertyName("supportedCapabilities")] + public required IList SupportedCapabilities { get; set; } + + /// The subset of the caller's bounded extensible capability identifiers this runtime cannot honour. + [JsonPropertyName("unsupportedCapabilities")] + public required IList UnsupportedCapabilities { get; set; } } -/// A single top-level key to record in the host's machine-wide state. The write replaces only that key and leaves the rest of the document untouched, so two writers recording different one-off flags do not overwrite each other. The stored credential keys cannot be written through this method. +/// The request was rejected because a bounded field fell outside its permitted range or a required field was unusable. Pagination may also be rejected by the authority after a continuation request; repeat the search without page. +/// The invalid-request variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GlobalStateWriteKeyRequest +public partial class SkillInstallationResultInvalidRequest : SkillInstallationResult { - /// Copilot configuration directory to write the state document in, taking precedence over the server's own `COPILOT_HOME` and default home. Omit it, or pass an empty string, to write the directory the server resolved for itself. Mirrors `globalState.loadForConfigDir`, so a caller can read and write the same directory. - [JsonPropertyName("configDir")] - public string? ConfigDir { get; set; } + /// + [JsonIgnore] + public override string Kind => "invalid-request"; - /// Top-level key to write, named as it appears in the result of `globalState.load`. It must be one of the writable keys that `globalState.writeKey` lists. - [JsonPropertyName("key")] - public string Key { get; set; } = string.Empty; + /// Which request field was rejected. + [JsonPropertyName("field")] + public required CatalogInvalidRequestField Field { get; set; } - /// Value to store for the key. Omit it, or pass null, to remove the key instead. - [JsonPropertyName("value")] - public JsonElement? Value { get; set; } + /// Human-readable explanation, safe to surface. Never echoes the offending value, nor a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } } -/// A literal choice the command input accepts, with a human-facing description. +/// Applies exactly one retained verified Skill installation plan. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SlashCommandInputChoice +internal sealed class SkillApplyInstallRequest { - /// Human-readable description shown alongside the choice. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; + /// Required authenticated bound catalogue and Skill installation capabilities. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); set; } - /// The literal choice value (e.g. 'on', 'off', 'show'). - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Opaque original plan, consumed once. + [JsonPropertyName("planHandle")] + public string PlanHandle { get; set; } = string.Empty; + + /// Same existing selected session as planning. + [JsonPropertyName("policySessionId")] + public string PolicySessionId { get; set; } = string.Empty; } -/// Optional unstructured input hint. +/// Read-only preparation of one owned Skill removal under fresh selected-session authority. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SlashCommandInput +internal sealed class SkillPlanUninstallRequest { - /// Optional literal choices the input accepts, each with a human-facing description; clients may render these as selectable options. - [JsonPropertyName("choices")] - public IList? Choices { get; set; } + /// Required authenticated bound installation contract. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); set; } - /// Optional completion hint for the input (e.g. 'directory' for filesystem path completion). - [JsonPropertyName("completion")] - public SlashCommandInputCompletion? Completion { get; set; } + /// Exact receipt to inspect. + [JsonPropertyName("installationId")] + public string InstallationId { get; set; } = string.Empty; - /// Hint to display when command input has not been provided. - [JsonPropertyName("hint")] - public string Hint { get; set; } = string.Empty; + /// Existing selected local session on this connection. + [JsonPropertyName("policySessionId")] + public string PolicySessionId { get; set; } = string.Empty; +} - /// When true, clients should pass the full text after the command name as a single argument rather than splitting on whitespace. - [JsonPropertyName("preserveMultilineInput")] - public bool? PreserveMultilineInput { get; set; } +/// One-use application of the exact retained Skill removal plan. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SkillApplyUninstallRequest +{ + /// Required authenticated bound installation contract. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); set; } - /// When true, the command requires non-empty input; clients should render the input hint as required. - [JsonPropertyName("required")] - public bool? Required { get; set; } + /// Opaque original removal plan, consumed once. + [JsonPropertyName("planHandle")] + public string PlanHandle { get; set; } = string.Empty; + + /// Same existing selected session as removal preparation. + [JsonPropertyName("policySessionId")] + public string PolicySessionId { get; set; } = string.Empty; } -/// Slash-command metadata with name, aliases, description, kind, input hint, execution allowance, and schedulability. +/// Server-side skill metadata, including name, description, source, enabled/invocable state, path, project path, and argument hint. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SlashCommandInfo +public sealed class ServerSkill { - /// Canonical aliases without leading slashes. - [JsonPropertyName("aliases")] - public IList? Aliases { get; set; } + /// Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field. + [JsonPropertyName("argumentHint")] + public string? ArgumentHint { get; set; } - /// Whether the command may run while an agent turn is active. - [JsonPropertyName("allowDuringAgentExecution")] - public bool AllowDuringAgentExecution { get; set; } + /// Canonical slash command name used to invoke the skill, without the leading '/'. + [JsonPropertyName("commandName")] + public string? CommandName { get; set; } - /// Human-readable command description. + /// Description of what the skill does. [JsonPropertyName("description")] public string Description { get; set; } = string.Empty; - /// Whether the command is experimental. - [JsonPropertyName("experimental")] - public bool? Experimental { get; set; } - - /// Optional unstructured input hint. - [JsonPropertyName("input")] - public SlashCommandInput? Input { get; set; } - - /// Coarse command category for grouping and behavior: runtime built-in, skill-backed command, or SDK/client-owned command. - [JsonPropertyName("kind")] - public SlashCommandKind Kind { get; set; } + /// Whether the skill is currently enabled (based on global config). + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } - /// Canonical command name without a leading slash. + /// Unique identifier for the skill. [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; - /// Whether the command may be the target of `/every` / `/after` schedules. Resolution happens at every tick, so only set this when the command is safe to re-invoke and produces an agent prompt. - [JsonPropertyName("schedulable")] - public bool? Schedulable { get; set; } + /// Absolute path to the skill file. + [JsonPropertyName("path")] + public string? Path { get; set; } + + /// The project path this skill belongs to (only for project/inherited skills). + [JsonPropertyName("projectPath")] + public string? ProjectPath { get; set; } + + /// Source location type (e.g., project, personal-copilot, plugin, builtin). + [JsonPropertyName("source")] + public SkillSource Source { get; set; } + + /// Whether the skill can be invoked by the user as a slash command. + [JsonPropertyName("userInvocable")] + public bool UserInvocable { get; set; } } -/// Slash commands available in the session, after applying any include/exclude filters. +/// Skills discovered across global and project sources. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CommandList +public sealed class ServerSkillList { - /// Commands available in this session. - [JsonPropertyName("commands")] - public IList Commands { get => field ??= []; set; } + /// Messages for skills that failed to load (e.g. malformed SKILL.md). Empty when host skills are excluded so host-local paths are not disclosed to multitenant callers. + [JsonPropertyName("errors")] + public IList? Errors { get; set; } + + /// All discovered skills across all sources. + [JsonPropertyName("skills")] + public IList Skills { get => field ??= []; set; } } -/// A single user setting's effective value alongside its default, so consumers can render settings left at their default. +/// Optional project paths and additional skill directories to include in discovery. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UserSettingMetadata +internal sealed class SkillsDiscoverRequest { - /// The centrally-known default for this setting (null when no default is registered). - [JsonPropertyName("default")] - public JsonElement Default { get; set; } + /// When true, omit skills from the host's global sources (personal, custom, plugin, and built-in), returning only project-scoped skills. For multitenant deployments. + [JsonPropertyName("excludeHostSkills")] + public bool? ExcludeHostSkills { get; set; } - /// True when the user has not set an explicit value for this setting (i.e. it is left at its default). Reflects whether the user has overridden the key, not whether the effective value happens to equal the default — a key explicitly set to a value identical to the default still reports false. - [JsonPropertyName("isDefault")] - public bool IsDefault { get; set; } + /// Optional skill scan paths to exclude from discovery. + [JsonPropertyName("ignoredSkillsLocations")] + public IList? IgnoredSkillsLocations { get; set; } - /// The effective value: the user's value if set, otherwise the default. - [JsonPropertyName("value")] - public JsonElement Value { get; set; } -} + /// Optional list of project directory paths to scan for project-scoped skills. + [JsonPropertyName("projectPaths")] + public IList? ProjectPaths { get; set; } -/// Per-key metadata for every known user setting in settings.json, including settings left at their default. Excludes repository- and enterprise-managed overrides. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UserSettingsGetResult -{ - /// Every known user setting keyed by setting name, each with its effective value, default, and whether it is at the default. - [JsonPropertyName("settings")] - public IDictionary Settings { get => field ??= new Dictionary(); set; } + /// Explicit discovery-only skill roots, not filesystem grants. Available without ambient discovery, subject to skill/managed disables. Relative paths resolve against the request's effective working directory when supplied. Null and omission are equivalent; an empty array contributes no explicit roots. + [JsonPropertyName("skillDirectories")] + public IList? SkillDirectories { get; set; } } -/// Partial user settings to write to settings.json. Each top-level key is written individually, replacing the existing value; a key whose value is null is removed. +/// Canonical directory where skills can be discovered or created, with scope, preference, and optional project path. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UserSettingsSetRequest +public sealed class SkillDiscoveryPath { - /// Partial user settings to write, as a free-form object keyed by setting name. - [JsonPropertyName("settings")] - public JsonElement Settings { get; set; } -} + /// Absolute path of the create/discovery target (may not exist on disk yet). + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; -/// Owner, name, and host of a GitHub repository, as resolved from a git remote URL. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class GitHubRepositoryIdentity -{ - /// Host the remote points at, for example `github.com` or a GitHub Enterprise hostname. - [JsonPropertyName("host")] - public string Host { get; set; } = string.Empty; + /// Whether this is the canonical directory to create a new skill in its tier. At most one entry per tier is preferred; the `personal-agents` and `custom` scopes are never preferred. + [JsonPropertyName("preferredForCreation")] + public bool PreferredForCreation { get; set; } - /// Repository name, without the owner prefix or the `.git` suffix. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// The input project path this directory was derived from (only for project scope). + [JsonPropertyName("projectPath")] + public string? ProjectPath { get; set; } - /// Repository owner login (user or organization). - [JsonPropertyName("owner")] - public string Owner { get; set; } = string.Empty; + /// Which tier this directory belongs to. + [JsonPropertyName("scope")] + public SkillDiscoveryScope Scope { get; set; } } -/// The GitHub repository that owns the requested path, when the selected remote (`origin`, else the first) is on a GitHub host. +/// Canonical locations where skills can be created so the runtime will recognize them. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitHubRepositoryAtPathResult +public sealed class SkillDiscoveryPathList { - /// Resolved repository identity, or null when the selected remote resolves to no GitHub host. - [JsonPropertyName("repository")] - public GitHubRepositoryIdentity? Repository { get; set; } + /// Canonical skill create/discovery directories, in priority order. + [JsonPropertyName("paths")] + public IList Paths { get => field ??= []; set; } } -/// Working-tree path whose owning GitHub repository should be resolved. +/// Optional project paths to enumerate. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitHubRepositoryAtPathRequest +public sealed class SkillsGetDiscoveryPathsRequest { - /// Absolute path to a directory inside the git working tree to resolve. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// When true, omit the host's personal and custom skill directories, leaving only project directories. For multitenant deployments. + [JsonPropertyName("excludeHostSkills")] + public bool? ExcludeHostSkills { get; set; } + + /// Optional skill scan paths to exclude from discovery. + [JsonPropertyName("ignoredSkillsLocations")] + public IList? IgnoredSkillsLocations { get; set; } + + /// Optional list of project directory paths. When omitted or empty, only personal and custom directories are returned. + [JsonPropertyName("projectPaths")] + public IList? ProjectPaths { get; set; } + + /// Explicit discovery-only skill roots to enumerate, including configured missing directories. Relative paths resolve against the request's effective working directory when supplied. These are not filesystem grants or preferred creation targets. Null and omission are equivalent. + [JsonPropertyName("skillDirectories")] + public IList? SkillDirectories { get; set; } } -/// A freshly registered request id. Registering it before the listing starts is what lets a cancel that races the request still find the owner listing slot. The id serves one listing only. Long-abandoned unused ids can be released by later allocations. +/// Inventory request under an explicitly selected existing session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitHubOwnersRequestIdResult +internal sealed class SkillInstallationsRequest { - /// Request id to pass to `gitHubOwners.list` and, to abandon it, `gitHubOwners.cancel`. - [JsonPropertyName("requestId")] - public long RequestId { get; set; } + /// Required authenticated bound installation contract. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); set; } + + /// Existing selected local session on this connection. + [JsonPropertyName("policySessionId")] + public string PolicySessionId { get; set; } = string.Empty; } -/// A GitHub login the authenticated user may act as: their own account, or an organization they belong to. +/// Existing-operation control. A new session selector is deliberately not accepted. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitHubOwnerOption +internal sealed class SkillInstallationOperationRequest { - /// The owner's GitHub login. - [JsonPropertyName("login")] - public string Login { get; set; } = string.Empty; + /// Required authenticated bound Skill installation capability. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); set; } - /// Which kind of owner this is. The authenticated user's own account is always reported as `user`. - [JsonPropertyName("type")] - public string Type { get; set; } = string.Empty; + /// Exact runtime-issued operation ID on the original connection. + [JsonPropertyName("operationId")] + public string OperationId { get; set; } = string.Empty; } -/// Outcome of an owner listing. Exactly one of `owners` and `message` is present, except that `throwError` reports a failure the caller is expected to raise rather than render. +/// Persisted enablement update for one owned Skill installation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitHubOwnersListResult +internal sealed class SkillSetEnabledRequest { - /// Why no owners could be listed, phrased for a user. Present when the listing failed in a way the caller should render rather than raise. - [JsonPropertyName("message")] - public string? Message { get; set; } + /// Required authenticated bound Skill installation capability. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); set; } - /// The owners, on success: the authenticated user first, then the organizations they belong to. - [JsonPropertyName("owners")] - public IList? Owners { get; set; } + /// Persisted enablement value. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } - /// A malformed request or an unreadable credential, which the caller raises instead of rendering. Kept a field rather than a dispatch error so it stays distinct from `message`, which the caller renders. - [JsonPropertyName("throwError")] - public string? ThrowError { get; set; } + /// Exact receipt identity to update. + [JsonPropertyName("installationId")] + public string InstallationId { get; set; } = string.Empty; - /// A line the caller should log. Present only alongside `message`, and only for failures worth recording. - [JsonPropertyName("warning")] - public string? Warning { get; set; } + /// Existing selected local session to reconcile after persistence. + [JsonPropertyName("policySessionId")] + public string PolicySessionId { get; set; } = string.Empty; } -/// Credential to list owners under, and the request id that makes the listing cancellable. +/// Skill names to mark as disabled in global configuration, replacing any previous list. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitHubOwnersListRequest +internal sealed class SkillsConfigSetDisabledSkillsRequest { - /// The credential the listing runs under, carried opaquely because its shape is the host's own and the runtime only resolves a token and a GitHub host from it. No credential travels: this selects one the runtime already holds. - [JsonPropertyName("authInfo")] - public JsonElement AuthInfo { get; set; } - - /// Request id from `gitHubOwners.nextRequestId`. An id that was never registered, canceled before use, released after being abandoned, or already used is refused rather than silently running uncancellable. - [JsonPropertyName("requestId")] - public long RequestId { get; set; } + /// List of skill names to disable. + [JsonPropertyName("disabledSkills")] + public IList DisabledSkills { get => field ??= []; set; } } -/// Whether the id named a running owner listing. +/// Adds or removes a single skill from the global disabled list, leaving every other entry untouched. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitHubOwnersCancelResult +internal sealed class SkillsConfigSetSkillDisabledRequest { - /// True when a listing with the id was running and the cancel stopped it. False when the id was never registered, was registered but unused, was released after being abandoned, or its listing had ended. An unused id is released and cannot start a later listing. - [JsonPropertyName("canceled")] - public bool Canceled { get; set; } + /// True to disable the skill, false to enable it. + [JsonPropertyName("disabled")] + public bool Disabled { get; set; } + + /// Name of the skill to add to or remove from the disabled list. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// The owner listing to abandon. +/// Outcome of a catalog.search call: either bounded inert candidates, or one typed refusal. Never a partial success. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitHubOwnersCancelRequest +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(CatalogSearchResultSucceeded), "succeeded")] +[JsonDerivedType(typeof(CatalogSearchResultNegotiationRefused), "negotiation-refused")] +[JsonDerivedType(typeof(CatalogSearchResultUnsupportedKind), "unsupported-kind")] +[JsonDerivedType(typeof(CatalogSearchResultInvalidRequest), "invalid-request")] +[JsonDerivedType(typeof(CatalogSearchResultAuthenticationRequired), "authentication-required")] +[JsonDerivedType(typeof(CatalogSearchResultPolicyRejected), "policy-rejected")] +[JsonDerivedType(typeof(CatalogSearchResultNetworkFailure), "network-failure")] +[JsonDerivedType(typeof(CatalogSearchResultUnsafeRetrieval), "unsafe-retrieval")] +[JsonDerivedType(typeof(CatalogSearchResultMalformedCard), "malformed-card")] +[JsonDerivedType(typeof(CatalogSearchResultContractViolation), "contract-violation")] +[JsonDerivedType(typeof(CatalogSearchResultUnavailable), "unavailable")] +public partial class CatalogSearchResult { - /// Request id the listing was started with. - [JsonPropertyName("requestId")] - public long RequestId { get; set; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// The remote the checked-out branch tracks. + +/// One inert catalog result, represented as an MCP server, discovery-only AI skill, or opt-in Agent Plugin variant so kind, media type, provenance, and available operations cannot contradict each other. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitCurrentBranchRemoteResult +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(CatalogCandidateMcpServer), "mcp-server")] +[JsonDerivedType(typeof(CatalogCandidateAiSkill), "ai-skill")] +[JsonDerivedType(typeof(CatalogCandidatePlugin), "plugin")] +public partial class CatalogCandidate { - /// Name of the tracked remote. Reports `origin` whenever the working tree has no tracking configuration to read, including on a detached HEAD, so this is never null and never empty. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("remote")] - public string Remote { get; set; } = string.Empty; + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// Working-tree path a git query applies to. + +/// Where and when an MCP server catalog reference was observed. Discovery provenance deliberately carries no content digest because search does not establish the exact validated content a later plan will bind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitCwdRequest +public sealed class CatalogMcpServerCandidateProvenance { - /// Absolute path to a directory inside the git working tree to query. + /// Host of the catalog authority that advertised the reference, without path, query, or credentials. Inert untrusted data. [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] [MinLength(1)] - [JsonPropertyName("cwd")] - public string Cwd { get; set; } = string.Empty; -} - -/// Updated working directory and git context. Emitted as the new payload of `session.context_changed`. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionWorkingDirectoryContext -{ - /// Merge-base commit SHA (fork point from the remote default branch). - [JsonPropertyName("baseCommit")] - public string? BaseCommit { get; set; } - - /// Current git branch name. - [JsonPropertyName("branch")] - public string? Branch { get; set; } - - /// Current working directory path. - [JsonPropertyName("cwd")] - public string Cwd { get; set; } = string.Empty; - - /// Root directory of the git repository, resolved via git rev-parse. - [JsonPropertyName("gitRoot")] - public string? GitRoot { get; set; } - - /// Head commit of the current git branch. - [JsonPropertyName("headCommit")] - public string? HeadCommit { get; set; } - - /// Hosting platform type of the repository. - [JsonPropertyName("hostType")] - public SessionWorkingDirectoryContextHostType? HostType { get; set; } + [JsonPropertyName("authority")] + public string Authority { get; set; } = string.Empty; - /// Repository identifier derived from the git remote URL ("owner/name" for GitHub, "org/project/repo" for Azure DevOps). - [JsonPropertyName("repository")] - public string? Repository { get; set; } + /// JSON MCP media type advertised for the referenced card. + [JsonPropertyName("mediaType")] + public McpServerCardMediaType MediaType { get; set; } - /// Raw host string from the git remote URL (e.g. "github.com", "dev.azure.com"). - [JsonPropertyName("repositoryHost")] - public string? RepositoryHost { get; set; } + /// ISO 8601 timestamp at which the runtime observed the catalog reference. This is not a retrieval or validation timestamp. + [JsonPropertyName("observedAt")] + public string ObservedAt { get; set; } = string.Empty; } -/// A GitHub repository one of a working tree's remotes points at. +/// Where a candidate's card came from. Exactly one of a URL or embedded data: the union has no variant carrying both, and no variant carrying neither, so the rule holds structurally rather than by validation. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitRemoteRepository +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(CatalogCandidateSourceUrl), "url")] +[JsonDerivedType(typeof(CatalogCandidateSourceEmbedded), "embedded")] +public partial class CatalogCandidateSource { - /// GitHub host serving the repository, which is not `github.com` for a GitHub Enterprise remote. - [JsonPropertyName("host")] - public string Host { get; set; } = string.Empty; - - /// Repository name, without the owner. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - /// Account or organization owning the repository. - [JsonPropertyName("owner")] - public string Owner { get; set; } = string.Empty; - - /// Name of the first remote that produced this distinct repository entry, such as `origin` or `upstream`. - [JsonPropertyName("remoteName")] - public string RemoteName { get; set; } = string.Empty; + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// The GitHub repositories a working tree's remotes point at. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitReposFromRemotesResult -{ - /// One entry per distinct GitHub repository, in the order git reports the first remote for each repository. Empty when no remote points at a GitHub host, which a caller should read as `not connected to GitHub`. Failing to read the remotes is an error, not an empty list. - [JsonPropertyName("repositories")] - public IList Repositories { get => field ??= []; set; } -} -/// Git working tree whose GitHub remotes should be listed. +/// Candidate whose card is retrieved from a URL through the runtime's hardened fetch boundary. +/// The url variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitReposFromRemotesRequest +public partial class CatalogCandidateSourceUrl : CatalogCandidateSource { - /// Absolute path to the root of the git working tree. + /// + [JsonIgnore] + public override string Kind => "url"; + + /// Card URL as advertised. Inert untrusted data: the runtime retrieves it only through its own hardened boundary, and it is never logged. [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] [MinLength(1)] - [JsonPropertyName("gitRoot")] - public string GitRoot { get; set; } = string.Empty; + [JsonPropertyName("url")] + public required string Url { get; set; } } -/// Validated device-managed settings discovered before a session exists. +/// Candidate whose card reference arrived inline. The document and its content-derived properties stay behind the runtime boundary. +/// The embedded variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedSettingsReadResult +public partial class CatalogCandidateSourceEmbedded : CatalogCandidateSource { - /// Discovery or validation error text when managed settings could not be read safely. - [JsonPropertyName("errorMessage")] - public string? ErrorMessage { get; set; } - - /// Validated, canonical managed-settings JSON. Omitted when no managed settings were discovered or when discovered settings failed validation. - [JsonPropertyName("settingsJson")] - public JsonElement? SettingsJson { get; set; } + /// + [JsonIgnore] + public override string Kind => "embedded"; } -/// One validation finding for a managed-settings document. +/// An inert MCP server catalog result. Every free-text field is untrusted external data and must never be treated as an instruction, and the handle is the only way to refer to the candidate in a later operation. +/// The mcp-server variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedSettingsDiagnostic +public partial class CatalogCandidateMcpServer : CatalogCandidate { - /// Human-readable description of the finding. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "mcp-server"; - /// Dot-separated path of the offending setting, such as `autoTier.overridable`. Empty for the document as a whole. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Description taken verbatim from the card. Inert untrusted text. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("description")] + public string? Description { get; set; } - /// Whether the finding rejects the document. - [JsonPropertyName("severity")] - public ManagedSettingsDiagnosticSeverity Severity { get; set; } -} + /// Display name taken verbatim from the card. Inert untrusted text. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(200)] + [JsonPropertyName("displayName")] + public required string DisplayName { get; set; } -/// One managed-settings channel and the document it delivered. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedSettingsLayer -{ - /// Validated managed-settings document this channel delivered. Absent when the channel delivered none. - [JsonPropertyName("settings")] - public JsonElement? Settings { get; set; } + /// Opaque, runtime-instance scoped, TTL-bound, single-use handle for this candidate. Carries no readable information and is rejected when stale, replayed, or presented to a different runtime instance. Never logged. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("handle")] + public required string Handle { get; set; } - /// Channel identifier: `device` (MDM, plist, registry, or managed file), `server` (account or organization policy), or `policyHelper` (session-local helper output, supported by compose). Treat unknown output values as additional channels; more may be added. - [JsonPropertyName("source")] - public string Source { get; set; } = string.Empty; -} + /// ISO 8601 timestamp after which the handle is stale and will be rejected. + [JsonPropertyName("handleExpiresAt")] + public required string HandleExpiresAt { get; set; } -/// Lock state and provenance of one managed setting. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedSettingMeta -{ - /// Whether users and repositories may choose a different value. `false` means policy locks the value. - [JsonPropertyName("overridable")] - public bool Overridable { get; set; } + /// Whether this MCP server can be planned for installation, and if policy prevents it. + [JsonPropertyName("installability")] + public required CatalogMcpServerInstallability Installability { get; set; } - /// Original managed value when the runtime adjusted it to a supported effective value. Omitted when no adjustment was needed. - [JsonPropertyName("requested")] - public string? Requested { get; set; } + /// JSON MCP media type of the underlying card. + [JsonPropertyName("mediaType")] + public required McpServerCardMediaType MediaType { get; set; } - /// Channel that supplied this scalar value, matching a `layers[].source`: `device`, `server`, or `policyHelper`. These scalar defaults select one winning channel, not a mixed source. Treat unknown values as additional channels; more may be added. + /// Where the catalog reference was observed, without the card itself or any content digest. + [JsonPropertyName("provenance")] + public required CatalogMcpServerCandidateProvenance Provenance { get; set; } + + /// Publisher taken verbatim from the card. Inert untrusted text. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(200)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("publisher")] + public string? Publisher { get; set; } + + /// Where the card came from: exactly one of a URL or embedded data, encoded as a tagged union so neither both nor neither can be represented. [JsonPropertyName("source")] - public string Source { get; set; } = string.Empty; + public required CatalogCandidateSource Source { get; set; } + + /// Versioned trust metadata observed from the catalog authority. Optional for protocol-3 compatibility with runtimes that predate trust snapshots. A trust-capable runtime emits an explicit snapshot even when the authority omitted or malformed its trust field. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("trust")] + public CatalogTrustSnapshot? Trust { get; set; } } -/// Per-key lock state and provenance for `ManagedSettingsValues`, with the same field names. Producers emit each typed key in values and meta together; both outer objects are omitted when no typed key is set. +/// Where and when an AI skill catalog reference was observed. Discovery provenance deliberately carries no content digest because search does not establish the exact validated content a later plan will bind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedSettingsMeta +public sealed class CatalogAiSkillCandidateProvenance { - /// Lock state and provenance of `values.autoTier`. - [JsonPropertyName("autoTier")] - public ManagedSettingMeta? AutoTier { get; set; } - - /// Lock state and provenance of `values.contextTier`. - [JsonPropertyName("contextTier")] - public ManagedSettingMeta? ContextTier { get; set; } + /// Host of the catalog authority that advertised the reference, without path, query, or credentials. Inert untrusted data. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("authority")] + public string Authority { get; set; } = string.Empty; - /// Lock state and provenance of `values.effortLevel`. - [JsonPropertyName("effortLevel")] - public ManagedSettingMeta? EffortLevel { get; set; } + /// Media type advertised for the referenced AI skill card. + [JsonPropertyName("mediaType")] + public CatalogAiSkillMediaType MediaType { get; set; } - /// Lock state and provenance of `values.model`. - [JsonPropertyName("model")] - public ManagedSettingMeta? Model { get; set; } + /// ISO 8601 timestamp at which the runtime observed the catalog reference. This is not a retrieval or validation timestamp. + [JsonPropertyName("observedAt")] + public string ObservedAt { get; set; } = string.Empty; } -/// Effective enterprise managed settings and contributing channels. Session events report applied policy; sessionless resolve reports an account/device snapshot, and compose reports a non-applying preview of candidate documents. Device values take precedence over server values, then the policy helper, per ordinary key, while permissions compose restrictively. Session-local SDK-client policy is included only in session results. Marked experimental while the managed-settings surface stabilizes. +/// An inert AI skill catalog result. Verified Skill candidates may be installable only when the runtime reports installability and the selected session is permitted to plan installation. +/// The ai-skill variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedSettingsResolvedData +public partial class CatalogCandidateAiSkill : CatalogCandidate { - /// Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers, and forced on when `failClosed` is true. - [JsonPropertyName("bypassPermissionsDisabled")] - public bool BypassPermissionsDisabled { get; set; } - - /// Whether a session-local permissions layer injected by the SDK host was present. - [JsonPropertyName("clientManaged")] - public bool? ClientManaged { get; set; } + /// + [JsonIgnore] + public override string Kind => "ai-skill"; - /// Whether an actual device MDM/plist/registry/file managed-settings layer was present. - [JsonPropertyName("deviceManaged")] - public bool DeviceManaged { get; set; } + /// Description taken verbatim from the card. Inert untrusted text. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("description")] + public string? Description { get; set; } - /// Whether managed policy could not be determined (e.g. a failed server fetch) and the session fell back to the fail-closed restriction. When true, restrictions such as disabling bypass-permissions are enforced even though `settings` may be absent. - [JsonPropertyName("failClosed")] - public bool FailClosed { get; set; } + /// Display name taken verbatim from the card. Inert untrusted text. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(200)] + [JsonPropertyName("displayName")] + public required string DisplayName { get; set; } - /// The setting keys under enterprise management in the effective managed settings (e.g. `model`, `enabledPlugins`, `permissions`). Empty when no managed settings are in force. - [JsonPropertyName("managedKeys")] - public IList ManagedKeys { get => field ??= []; set; } + /// Opaque, runtime-instance scoped, TTL-bound, single-use handle for this candidate. Carries no readable information and is rejected when stale, replayed, or presented to a different runtime instance. Never logged. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("handle")] + public required string Handle { get; set; } - /// Whether at least two managed sources supplied permission allowlists, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. - [JsonPropertyName("permissionsAllowIntersected")] - public bool? PermissionsAllowIntersected { get; set; } + /// ISO 8601 timestamp after which the handle is stale and will be rejected. + [JsonPropertyName("handleExpiresAt")] + public required string HandleExpiresAt { get; set; } - /// Whether the policy-helper managed-settings layer was present. The policy helper is the weakest channel: it fills keys no enterprise source set and can never replace one. - [JsonPropertyName("policyHelperManaged")] - public bool? PolicyHelperManaged { get; set; } + /// Whether this AI skill candidate can be planned for verified installation in the selected session. + [JsonPropertyName("installability")] + public required CatalogAiSkillInstallability Installability { get; set; } - /// Whether the effective sandbox policy forces the sandbox on *only* because managed policy could not be determined, rather than because the policy requires it. Lets clients tell a user whose `--no-sandbox` was overridden that the sandbox stayed on as a fail-closed fallback, instead of attributing it to an administrator who set no such policy. - [JsonPropertyName("sandboxEnabledByUndeterminedPolicy")] - public bool? SandboxEnabledByUndeterminedPolicy { get; set; } + /// Media type of the underlying AI skill card. + [JsonPropertyName("mediaType")] + public required CatalogAiSkillMediaType MediaType { get; set; } - /// Whether the server (account/org) managed-settings layer was present. - [JsonPropertyName("serverManaged")] - public bool ServerManaged { get; set; } + /// Where the catalog reference was observed, without the card itself or any content digest. + [JsonPropertyName("provenance")] + public required CatalogAiSkillCandidateProvenance Provenance { get; set; } - /// The effective (resolved) managed settings values, so clients can render exactly what is enforced. Absent when no managed policy is in force. - [JsonPropertyName("settings")] - public JsonElement? Settings { get; set; } + /// Publisher taken verbatim from the card. Inert untrusted text. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(200)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("publisher")] + public string? Publisher { get; set; } - /// Channel summary: `server`, `device`, `client`, or `policyHelper` when exactly one channel contributed; `mixed` when multiple channels contributed; otherwise `none`. Consult the per-channel booleans for exact provenance. + /// Where the card came from: exactly one of a URL or embedded data, encoded as a tagged union so neither both nor neither can be represented. [JsonPropertyName("source")] - public ManagedSettingsResolvedSource Source { get; set; } + public required CatalogCandidateSource Source { get; set; } + + /// Versioned trust metadata observed from the catalog authority. Optional for protocol-3 compatibility with runtimes that predate trust snapshots. A trust-capable runtime emits an explicit snapshot even when the authority omitted or malformed its trust field. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("trust")] + public CatalogTrustSnapshot? Trust { get; set; } } -/// Typed effective values of managed settings. Each field mirrors the managed-settings schema key of the same name; more keys are added as they are typed. +/// Where and when an Agent Plugin catalog reference was observed. Discovery provenance deliberately carries no descriptor URL, raw data, candidate handle, or content digest. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedSettingsValues +public sealed class CatalogAgentPluginCandidateProvenance { - /// Managed Auto routing preference, used when the selected model is `auto`. - [JsonPropertyName("autoTier")] - public AutoTier? AutoTier { get; set; } + /// Host of the catalog authority that advertised the reference, without path, query, or credentials. Inert untrusted data. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("authority")] + public string Authority { get; set; } = string.Empty; - /// Managed context-tier default for the managed concrete model. - [JsonPropertyName("contextTier")] - public ContextTier? ContextTier { get; set; } + /// Canonical Agent Plugin media type. + [JsonPropertyName("mediaType")] + public CatalogAgentPluginMediaType MediaType { get; set; } - /// Managed reasoning-effort default for the managed concrete model. The runtime clamps it to an entitled effort when model availability is known. - [JsonPropertyName("effortLevel")] - public string? EffortLevel { get; set; } + /// ISO 8601 timestamp at which the runtime observed the catalog reference. This is not a retrieval or validation timestamp. + [JsonPropertyName("observedAt")] + public string ObservedAt { get; set; } = string.Empty; +} - /// Managed default model identifier. When model availability was resolved, aliases and family names are projected to a concrete available model ID; otherwise the configured value is returned. - [JsonPropertyName("model")] - public string? Model { get; set; } +/// Syntactically validated GitHub repository provenance declared by catalog metadata. This is a source claim rather than proof that the descriptor URL resolves to the repository. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class CatalogPluginRepositorySource +{ + /// Case-preserving safe relative POSIX path derived from metadata.repoPath. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(256)] + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + + /// Canonical lowercase owner/repository name derived from metadata.sourceSet. + [RegularExpression("^[a-z0-9_.-]+/[a-z0-9_.-]+$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(3)] + [MaxLength(256)] + [JsonPropertyName("repository")] + public string Repository { get; set; } = string.Empty; } -/// Effective enterprise managed settings for an account, resolved without a session. +/// An inert Agent Plugin catalog result. Its canonical catalog identity, declared version, repository source claim, and explicit compatibility tags are safe to correlate, while its descriptor, URL, raw data, and installed-plugin state remain runtime-private. This contract-only variant does not mint or expose a candidate handle. +/// The plugin variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedSettingsResolveResult +public partial class CatalogCandidatePlugin : CatalogCandidate { - /// Printable opaque identity of the account the settings were resolved for, suitable for comparison and storage, not an account selectionId. Absent when no account was available, in which case only device policy is reported. - [JsonPropertyName("account")] - public string? Account { get; set; } + /// + [JsonIgnore] + public override string Kind => "plugin"; - /// Warnings about unavailable policy sources or a failed refresh served from cache. A cached response is not proof of a successful live fetch; `resolved.failClosed` separately describes enforcement. - [JsonPropertyName("diagnostics")] - public IList Diagnostics { get => field ??= []; set; } + /// Explicit validated compatibility tags, in canonical order. An empty list means the source declared no recognised compatibility; clients must not infer compatibility from other fields. `canvas-only` requires both `canvas` and `github-copilot`. + [JsonPropertyName("compatibilityTags")] + public required IList CompatibilityTags { get; set; } - /// Each managed-settings channel consulted, strongest first, with the validated document it delivered before merging. `resolved.settings` is the merged result. More channels may be added over time. - [JsonPropertyName("layers")] - public IList Layers { get => field ??= []; set; } + /// Description taken verbatim from the card. Inert untrusted text. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("description")] + public string? Description { get; set; } - /// Per-key lock state and provenance for the entries in `values`, using the same key names. - [JsonPropertyName("meta")] - public ManagedSettingsMeta? Meta { get; set; } + /// Display name taken verbatim from the card. Inert untrusted text. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(200)] + [JsonPropertyName("displayName")] + public required string DisplayName { get; set; } - /// Effective managed settings from the device and account (server) channels, in the same shape as `session.managedSettings.get`, excluding session-local injection. - [JsonPropertyName("resolved")] - public ManagedSettingsResolvedData Resolved { get => field ??= new(); set; } + /// Validated, normalised catalogue resource URN. This identity comes only from the catalog identifier and is never inferred from display text or installed-plugin state. + [JsonPropertyName("identity")] + public required string Identity { get; set; } - /// Typed effective values of managed settings, keyed like the managed-settings schema and already resolved across channels, with the `{ "overridable": ... }` wrapper removed. Present when policy sets at least one typed key. Keys not typed here are available in `resolved.settings`. - [JsonPropertyName("values")] - public ManagedSettingsValues? Values { get; set; } -} + /// Canonical Agent Plugin media type. + [JsonPropertyName("mediaType")] + public required CatalogAgentPluginMediaType MediaType { get; set; } -/// RPC data type for ManagedSettingsResolve operations. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ManagedSettingsResolveRequest -{ - /// Embedding client identity for server policy requests, as in session creation. Omit for the CLI identity. - [JsonPropertyName("clientName")] - public string? ClientName { get; set; } + /// Where the Agent Plugin catalog reference was observed, without its descriptor, URL, raw data, or content digest. + [JsonPropertyName("provenance")] + public required CatalogAgentPluginCandidateProvenance Provenance { get; set; } - /// GitHub token to resolve instead of the current account. The call fails when the token cannot be resolved. - [JsonPropertyName("gitHubToken")] - public string? GitHubToken { get; set; } + /// Publisher taken verbatim from the card. Inert untrusted text. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(200)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("publisher")] + public string? Publisher { get; set; } - /// Opaque account identifier returned by `account.getAllUsers`. When omitted, the current account is used, or device policy only when no account is signed in. - [JsonPropertyName("selectionId")] - public string? SelectionId { get; set; } + /// Bounded repository provenance declared through the catalog's sourceSet and repoPath metadata. It contains no descriptor URL. + [JsonPropertyName("source")] + public required CatalogPluginRepositorySource Source { get; set; } - /// Working directory used to run an organization policy helper. When omitted, sessionless resolution does not run the helper. - [JsonPropertyName("workingDirectory")] - public string? WorkingDirectory { get; set; } + /// Versioned trust metadata observed from the catalog authority. Optional for protocol-3 compatibility and emitted only when the caller also requires the trust-snapshot capability. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("trust")] + public CatalogTrustSnapshot? Trust { get; set; } + + /// Optional version declared by the catalog source. Omitted rather than guessed when the source supplies no version. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("version")] + public string? Version { get; set; } } -/// The authoring JSON schema for managed settings recognized by this runtime. +/// Authority-reported navigation metadata, returned only to callers requiring catalog-search-pagination and only when a supported token is present. Tokenless first-page and continuation responses omit this object; no counts are inferred from candidates. The opaque token may be retained for previous or numbered navigation even when hasNextPage is false. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedSettingsSchemaResult +public sealed class CatalogSearchPagination { - /// Version of the runtime that owns this schema. - [JsonPropertyName("runtimeVersion")] - public string RuntimeVersion { get; set; } = string.Empty; + /// One-based page returned by the authority. + [JsonPropertyName("currentPage")] + public int CurrentPage { get; set; } - /// JSON schema (draft 2020-12) with descriptive shared `x-composition` annotations, not a complete runtime composition contract. Model, effortLevel, and contextTier remain coupled; use `managedSettings.compose` for the runtime's effective result. - [JsonPropertyName("schema")] - public JsonElement Schema { get; set; } -} + /// Whether the authority token advertises a valid next target within the navigation window. Not inferred from token presence, truncated, or currentPage being less than pageCount. + [JsonPropertyName("hasNextPage")] + public bool HasNextPage { get; set; } -/// Result of validating a managed-settings document. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedSettingsValidateResult -{ - /// Errors that reject the document and warnings about content the runtime ignores. - [JsonPropertyName("diagnostics")] - public IList Diagnostics { get => field ??= []; set; } + /// Navigation window ceiling ceil(1000 / pageSize), not the number of existing pages. Legal targets must not exceed this ceiling or the token's signed pageCount. + [JsonPropertyName("maxPage")] + public int MaxPage { get; set; } - /// Canonical form of the document the runtime would apply, with unrecognized keys removed. Absent when the document is invalid. - [JsonPropertyName("settings")] - public JsonElement? Settings { get; set; } + /// Backend-reported page count, which may exceed maxPage. Present pagination metadata always describes a multi-page result; zero- and single-page responses omit pagination. Navigation targets must also be within the signed pageCount carried by the supplied token. + [JsonPropertyName("pageCount")] + public long PageCount { get; set; } - /// Whether the runtime would accept the document within the preview resource limits. Always equals whether `settings` is present. An invalid document is rejected as a whole. - [JsonPropertyName("valid")] - public bool Valid { get; set; } + /// Page size bound to the search, equal to the effective request limit. + [JsonPropertyName("pageSize")] + public int PageSize { get; set; } + + /// Opaque pagination token. Session-bound search returns an expiring runtime-owned reference retaining the exact private authority token, original search and authority. Legacy unbound search returns the authority token unchanged, without a runtime-created expiry. Only the runtime unwraps tokens or changes targetPage; SDK consumers must not decode, modify or log them. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(4096)] + [JsonPropertyName("token")] + public string Token { get; set; } = string.Empty; + + /// Backend-reported count for this response, not the number of returned candidates. Its relationship to the full query result set is unknown. + [JsonPropertyName("totalCount")] + public long TotalCount { get; set; } + + /// The relationship of totalCount to the complete query result set is unknown; neither exactness nor a lower-bound guarantee is implied. + [JsonPropertyName("totalCountRelation")] + public CatalogSearchTotalCountRelation TotalCountRelation { get; set; } } -/// A candidate managed-settings document to validate without applying it. +/// A completed catalog search containing inert candidate summaries. MCP server and AI skill variants carry a single-use handle; the Agent Plugin variant is handleless. +/// The succeeded variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ManagedSettingsValidateRequest +public partial class CatalogSearchResultSucceeded : CatalogSearchResult { - /// The document to validate: a JSON object, or a string containing the document's JSON text. Preview documents are limited to 1 MiB and 64 levels of nesting, a stricter resource limit than delivered-policy parsing; violations are returned as diagnostics. - [JsonPropertyName("content")] - public JsonElement Content { get; set; } + /// + [JsonIgnore] + public override string Kind => "succeeded"; - /// Channel the document is meant for (`device`, `server`, or `policyHelper`). Some keys are only honored in some channels; for example, a `policyHelper` registration is ignored in policy-helper output. When omitted, no channel-specific checks run. - [JsonPropertyName("layer")] - public string? Layer { get; set; } + /// Matching candidates, never more than the requested limit. All text is inert untrusted data. + [JsonPropertyName("candidates")] + public required IList Candidates { get; set; } + + /// Protocol version and capabilities the runtime honoured. + [JsonPropertyName("negotiated")] + public required CatalogNegotiatedContract Negotiated { get; set; } + + /// Navigation metadata for callers explicitly requiring catalog-search-pagination. Omitted when the authority returns no token, including tokenless first-page and continuation responses. Counts are never substituted from candidates.length. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("pagination")] + public CatalogSearchPagination? Pagination { get; set; } + + /// Pseudonymous identifier for this search, issued by the runtime or by the catalog authority it queried and never by the caller, so it cannot be forged or replayed to attribute an install to a search that never happened. Always present on a success, so a result set can be tied to the installs it leads to. It identifies a search rather than a person: it is derived from no user, account, device, or query data, and must never be joined with user identity to re-identify anyone. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(64)] + [JsonPropertyName("searchId")] + public required string SearchId { get; set; } + + /// Legacy indication that the authority returned a page token. Preserved for compatibility; this is not a has-next-page indicator. Use pagination.hasNextPage when pagination metadata is present. + [JsonPropertyName("truncated")] + public required bool Truncated { get; set; } } -/// The effective managed settings the runtime would enforce for the given documents, in the same shape `managedSettings.resolve` returns. +/// The caller's protocol version or required capabilities cannot be honoured. Returned instead of a partial or ambiguous success. +/// The negotiation-refused variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedSettingsComposeResult +public partial class CatalogSearchResultNegotiationRefused : CatalogSearchResult { - /// Warnings about ignored content, with paths prefixed by the channel name. - [JsonPropertyName("diagnostics")] - public IList Diagnostics { get => field ??= []; set; } + /// + [JsonIgnore] + public override string Kind => "negotiation-refused"; - /// Only the supplied channels, strongest first, with canonical documents. Empty canonical documents are represented as absent settings, as in live resolution. - [JsonPropertyName("layers")] - public IList Layers { get => field ??= []; set; } + /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } - /// Per-key lock state and provenance for `values`. - [JsonPropertyName("meta")] - public ManagedSettingsMeta? Meta { get; set; } + /// Lowest caller protocol version this runtime will serve. + [JsonPropertyName("minimumSupportedProtocolVersion")] + public required long MinimumSupportedProtocolVersion { get; set; } - /// Effective managed settings, in the same shape as `session.managedSettings.get`. - [JsonPropertyName("resolved")] - public ManagedSettingsResolvedData Resolved { get => field ??= new(); set; } + /// Whether the version or the capability set was the problem. + [JsonPropertyName("reason")] + public required CatalogNegotiationRefusedReason Reason { get; set; } - /// Typed effective values, as in `managedSettings.resolve`. - [JsonPropertyName("values")] - public ManagedSettingsValues? Values { get; set; } + /// Protocol version of the runtime that refused the request. + [JsonPropertyName("runtimeProtocolVersion")] + public required long RuntimeProtocolVersion { get; set; } + + /// Capabilities this runtime can safely advertise to this caller. The complete five-capability protocol-3 legacy set is always present; every capability added after that baseline appears only when the caller required it, so an older closed-enum decoder can still consume a refusal. This list does not imply that every deployment has enabled every operation. + [JsonPropertyName("supportedCapabilities")] + public required IList SupportedCapabilities { get; set; } + + /// The subset of the caller's bounded extensible capability identifiers this runtime cannot honour. + [JsonPropertyName("unsupportedCapabilities")] + public required IList UnsupportedCapabilities { get; set; } } -/// One candidate channel; absent settings represents a channel that delivered no document. +/// The request asked for a candidate kind this runtime does not serve. +/// The unsupported-kind variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedSettingsComposeLayer +public partial class CatalogSearchResultUnsupportedKind : CatalogSearchResult { - /// Candidate managed-settings document. Omit when the channel delivered none, as in resolve output. - [JsonPropertyName("settings")] - public JsonElement? Settings { get; set; } + /// + [JsonIgnore] + public override string Kind => "unsupported-kind"; - /// The channel whose candidate document is being supplied. - [JsonPropertyName("source")] - public ManagedSettingsChannel Source { get; set; } + /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } + + /// The kinds from the request that are not supported. + [JsonPropertyName("requestedKinds")] + public required IList RequestedKinds { get; set; } + + /// Every candidate kind this runtime can serve. + [JsonPropertyName("supportedKinds")] + public required IList SupportedKinds { get; set; } } -/// Candidate managed-settings documents to merge without applying them. +/// The request was rejected because a bounded field fell outside its permitted range or a required field was unusable. Pagination may also be rejected by the authority after a continuation request; repeat the search without page. +/// The invalid-request variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ManagedSettingsComposeRequest +public partial class CatalogSearchResultInvalidRequest : CatalogSearchResult { - /// One entry per channel. `source` must be `device`, `server`, or `policyHelper`, each at most once (checked at runtime); order does not matter, because channel precedence is fixed. To preview documents from resolve output, map recognized source strings to ManagedSettingsChannel and copy their settings; generated resolve and compose layer types are distinct. Omitted settings means this channel delivered no document. Supplied documents must be valid within the preview limits; warnings are returned in diagnostics. Compose does not reproduce source-failure state or retained enforcement floors from resolve. - [JsonPropertyName("layers")] - public IList Layers { get => field ??= []; set; } + /// + [JsonIgnore] + public override string Kind => "invalid-request"; + + /// Which request field was rejected. + [JsonPropertyName("field")] + public required CatalogInvalidRequestField Field { get; set; } + + /// Human-readable explanation, safe to surface. Never echoes the offending value, nor a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } } -/// Indicates whether the calling client was registered as the session filesystem provider. +/// An optional catalog authentication exchange did not establish the caller's identity. Anonymous search remains supported; this refusal is reserved for an operation that cannot continue after the attempted exchange. It is distinct from `policy-rejected` and from a network failure, and the reason identifies the recovery action. +/// The authentication-required variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsSetProviderResult +public partial class CatalogSearchResultAuthenticationRequired : CatalogSearchResult { - /// Whether the provider was set successfully. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// + [JsonIgnore] + public override string Kind => "authentication-required"; + + /// Human-readable explanation, safe to surface. Never contains a credential or token, nor a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } + + /// Why authentication failed. Only an expired credential justifies attempting a silent refresh; an absent or rejected credential requires sign-in. + [JsonPropertyName("reason")] + public required CatalogAuthenticationRequiredReason Reason { get; set; } } -/// Optional capabilities declared by the provider. +/// Registry or enterprise policy refused the operation. +/// The policy-rejected variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsSetProviderCapabilities +public partial class CatalogSearchResultPolicyRejected : CatalogSearchResult { - /// Whether the provider supports binary reads and writes through sessionFs.readFileBytes and sessionFs.writeFileBytes. - [JsonPropertyName("binary")] - public bool? Binary { get; set; } + /// + [JsonIgnore] + public override string Kind => "policy-rejected"; - /// Whether the provider supports SQLite query/exists operations. - [JsonPropertyName("sqlite")] - public bool? Sqlite { get; set; } + /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } + + /// Which authority produced the decision. + [JsonPropertyName("source")] + public required McpPlanPolicySource Source { get; set; } } -/// Initial working directory, session-state path layout, and path conventions used to register the calling SDK client as the session filesystem provider. A registered provider is authoritative for path interpretation and filesystem facts used by workspace permission validation. Paths are interpreted lexically; home-relative paths (`~` and `~/...`) and Windows drive-relative paths such as `C:foo` are unsupported. Until provider-side canonicalization is supported, providers must not expose symlinks inside allowed roots that escape those roots. +/// The runtime could not reach the catalog authority or retrieve a card. Covers being offline as well as transport-level failure. +/// The network-failure variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionFsSetProviderRequest +public partial class CatalogSearchResultNetworkFailure : CatalogSearchResult { - /// Optional capabilities declared by the provider. - [JsonPropertyName("capabilities")] - public SessionFsSetProviderCapabilities? Capabilities { get; set; } + /// + [JsonIgnore] + public override string Kind => "network-failure"; - /// Path conventions used by this filesystem. - [JsonPropertyName("conventions")] - public SessionFsSetProviderConventions Conventions { get; set; } + /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } - /// Absolute initial working directory for sessions. Registering the provider establishes this path as the root of its virtual namespace; the runtime does not require the provider to materialize or stat it before creating a session. - [JsonPropertyName("initialCwd")] - public string InitialCwd { get; set; } = string.Empty; + /// Categorised failure, low cardinality so it can be aggregated without carrying a URL. + [JsonPropertyName("reason")] + public required CatalogNetworkFailureReason Reason { get; set; } - /// Path within each session's SessionFs where the runtime stores files for that session. - [JsonPropertyName("sessionStatePath")] - public string SessionStatePath { get; set; } = string.Empty; + /// Bounded cooldown in seconds before another catalog request should be attempted, when the authority supplied a numeric Retry-After value or the runtime applied its documented fallback. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("retryAfterSeconds")] + public int? RetryAfterSeconds { get; set; } + + /// HTTP status code, when the failure was a rejected response. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("statusCode")] + public int? StatusCode { get; set; } } -/// Indicates whether the calling client was registered as the LLM inference provider. +/// Retrieval was refused by the runtime's hardened fetch boundary before any request left the process, or before a redirect was followed. +/// The unsafe-retrieval variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class LlmInferenceSetProviderResult +public partial class CatalogSearchResultUnsafeRetrieval : CatalogSearchResult { - /// Whether the provider was set successfully. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// + [JsonIgnore] + public override string Kind => "unsafe-retrieval"; + + /// Human-readable explanation, safe to surface. Never contains the refused URL, nor a query, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } + + /// Which control refused the retrieval, low cardinality so it can be aggregated without carrying a URL. + [JsonPropertyName("reason")] + public required CatalogUnsafeRetrievalReason Reason { get; set; } } -/// Whether the start frame was accepted. +/// A card could not be parsed or did not satisfy its declared media type's schema. +/// The malformed-card variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class LlmInferenceHttpResponseStartResult +public partial class CatalogSearchResultMalformedCard : CatalogSearchResult { - /// True when the response start was matched to a pending request; false when unknown. - [JsonPropertyName("accepted")] - public bool Accepted { get; set; } + /// + [JsonIgnore] + public override string Kind => "malformed-card"; + + /// Media type the card was interpreted as, when it declared one this runtime recognises. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("mediaType")] + public CatalogMediaType? MediaType { get; set; } + + /// Human-readable explanation, safe to surface. Never echoes card content, nor a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } + + /// How the card failed validation. + [JsonPropertyName("reason")] + public required CatalogMalformedCardReason Reason { get; set; } } -/// Response head. +/// An upstream catalog response broke the wire contract. Most importantly, every result must carry exactly one of a URL or embedded data: a result carrying both, or neither, is refused here rather than being guessed at. +/// The contract-violation variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class LlmInferenceHttpResponseStartRequest +public partial class CatalogSearchResultContractViolation : CatalogSearchResult { - /// HTTP response headers, preserving multiple values per name. - [JsonPropertyName("headers")] - public IDictionary> Headers { get => field ??= new Dictionary>(); set; } - - /// Matches the requestId from the originating httpRequestStart frame. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "contract-violation"; - /// HTTP status code. - [JsonPropertyName("status")] - public long Status { get; set; } + /// Human-readable explanation, safe to surface. Never echoes response content, nor a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } - /// Optional HTTP status reason phrase. - [JsonPropertyName("statusText")] - public string? StatusText { get; set; } + /// Which rule the response broke. + [JsonPropertyName("reason")] + public required CatalogContractViolationReason Reason { get; set; } } -/// Whether the chunk was accepted. +/// The operation is not available on this runtime. Distinct from a network failure: nothing was attempted. +/// The unavailable variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class LlmInferenceHttpResponseChunkResult +public partial class CatalogSearchResultUnavailable : CatalogSearchResult { - /// True when the chunk was matched to a pending request; false when unknown. - [JsonPropertyName("accepted")] - public bool Accepted { get; set; } + /// + [JsonIgnore] + public override string Kind => "unavailable"; + + /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } + + /// Why the operation is unavailable. + [JsonPropertyName("reason")] + public required CatalogUnavailableReason Reason { get; set; } } -/// Set to terminate the response with a transport-level failure. Implies end-of-stream; any further chunks for this requestId are ignored. +/// An explicit numbered-page request. SDK consumers treat the token as opaque. For bound search, the runtime unwraps an expiring owner-bound reference to the private authority token; only the runtime changes the authority token's targetPage. Legacy unbound navigation keeps its authority-issued token semantics. No snapshot stability is promised. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class LlmInferenceHttpResponseChunkError +public sealed class CatalogSearchPage { - /// Optional machine-readable error code. - [JsonPropertyName("code")] - public string? Code { get; set; } + /// Requested one-based page. Must not exceed either the token's signed pageCount or the navigation window ceil(1000 / pageSize). Repeat the search without page to discover newly available pages beyond that signed pageCount. + [JsonPropertyName("number")] + public int Number { get; set; } - /// Human-readable failure description. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; + /// Opaque pagination token from an earlier response, owner-bound when session-bound search was requested. Never decode, modify or log it in an SDK consumer. Expired or foreign bound references require a fresh bound search, not a legacy retry. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(4096)] + [JsonPropertyName("token")] + public string Token { get; set; } = string.Empty; } -/// A response body chunk or terminal error. +/// A bounded catalog search. Both the query length and the result count are capped by the schema so a caller cannot request an unbounded scan. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class LlmInferenceHttpResponseChunkRequest +public sealed class CatalogSearchRequest { - /// When true, `data` is base64-encoded bytes. When absent or false, `data` is UTF-8 text. - [JsonPropertyName("binary")] - public bool? Binary { get; set; } + /// Protocol version and capabilities the caller requires. + [JsonPropertyName("contract")] + public required CatalogClientContract Contract { get; set; } - /// Body byte range. UTF-8 text when `binary` is absent or false; base64-encoded bytes when `binary` is true. May be empty (e.g. when the response body is empty: send a single chunk with empty data and end=true). - [JsonPropertyName("data")] - public string Data { get; set; } = string.Empty; + /// Restrict results to these candidate kinds. Agent Plugins are opt-in and require the `agent-plugin-discovery` capability so protocol-v3 clients generated before that variant cannot receive an unknown result; when omitted, the backwards-compatible MCP server and AI skill kinds are searched. + [JsonPropertyName("kinds")] + public IList? Kinds { get; set; } - /// When true, this is the final body chunk for the response. The runtime treats the response body as complete after receiving an end-marked chunk. - [JsonPropertyName("end")] - public bool? End { get; set; } + /// Maximum number of candidates to return. Defaults to 10 when omitted. + [JsonPropertyName("limit")] + public int? Limit { get; set; } - /// Set to terminate the response with a transport-level failure. Implies end-of-stream; any further chunks for this requestId are ignored. - [JsonPropertyName("error")] - public LlmInferenceHttpResponseChunkError? Error { get; set; } + /// Numbered navigation using metadata from an earlier response. Requires catalog-search-pagination and the same query, kinds and effective limit. Omit for a fresh first-page search. + [JsonPropertyName("page")] + public CatalogSearchPage? Page { get; set; } - /// Matches the requestId from the originating httpRequestStart frame. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// + /// Select an existing attached local session. Requires authenticated, session-bound search. + /// The runtime never creates, resumes or reconfigures a session to honour this selector. + /// + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(256)] + [JsonPropertyName("policySessionId")] + public string? PolicySessionId { get; set; } + + /// Free-text search query. Persisted as tool input for session continuity, but omitted from telemetry. + [RegularExpression("\\S")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(256)] + [JsonPropertyName("query")] + public required string Query { get; set; } } -/// Pre-resolved working-directory context for session startup. +/// Typed outcome of catalog.select. Only the selected host result carries a fresh candidate handle; the model-facing projection removes both that handle and searchId. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionContext +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(CatalogSelectionResultSelected), "selected")] +[JsonDerivedType(typeof(CatalogSelectionResultDeclined), "declined")] +[JsonDerivedType(typeof(CatalogSelectionResultCancelled), "cancelled")] +[JsonDerivedType(typeof(CatalogSelectionResultTimedOut), "timed-out")] +[JsonDerivedType(typeof(CatalogSelectionResultInvalid), "invalid")] +[JsonDerivedType(typeof(CatalogSelectionResultStale), "stale")] +[JsonDerivedType(typeof(CatalogSelectionResultReplayed), "replayed")] +[JsonDerivedType(typeof(CatalogSelectionResultForeign), "foreign")] +[JsonDerivedType(typeof(CatalogSelectionResultWrongKind), "wrong-kind")] +[JsonDerivedType(typeof(CatalogSelectionResultNegotiationRefused), "negotiation-refused")] +[JsonDerivedType(typeof(CatalogSelectionResultInvalidRequest), "invalid-request")] +[JsonDerivedType(typeof(CatalogSelectionResultUnavailable), "unavailable")] +public partial class CatalogSelectionResult { - /// Active git branch. - [JsonPropertyName("branch")] - public string? Branch { get; set; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// Most recent working directory for this session. - [JsonPropertyName("cwd")] - public string Cwd { get; set; } = string.Empty; - /// Git repository root, if the cwd was inside a git repo. - [JsonPropertyName("gitRoot")] - public string? GitRoot { get; set; } +/// The chosen candidate was transferred into a fresh bounded single-use handle for a later explicit planning request. +/// The selected variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class CatalogSelectionResultSelected : CatalogSelectionResult +{ + /// + [JsonIgnore] + public override string Kind => "selected"; - /// Repository host type. - [JsonPropertyName("hostType")] - public SessionContextHostType? HostType { get; set; } + /// Fresh single-use candidate handle accepted by mcp.planInstall. Returned only to the native host and never included in model-tool output. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(256)] + [JsonPropertyName("candidateHandle")] + public required string CandidateHandle { get; set; } - /// Repository slug in `owner/name` form, when known. - [JsonPropertyName("repository")] - public string? Repository { get; set; } + /// The exact search identifier privately bound to the selected candidate. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(64)] + [JsonPropertyName("searchId")] + public required string SearchId { get; set; } } -/// GitHub repository the remote session belongs to. +/// The caller explicitly declined every candidate and the retained search state was released. +/// The declined variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class RemoteSessionMetadataRepository +public partial class CatalogSelectionResultDeclined : CatalogSelectionResult { - /// Branch associated with the remote session. - [JsonPropertyName("branch")] - public string Branch { get; set; } = string.Empty; - - /// Repository name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "declined"; - /// Repository owner. - [JsonPropertyName("owner")] - public string Owner { get; set; } = string.Empty; + /// The search identifier privately bound to the released selection group. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(64)] + [JsonPropertyName("searchId")] + public required string SearchId { get; set; } } -/// Remote session metadata for the session to hand off (typically obtained from `sessions.list` with `source: "remote"`). +/// The caller cancelled the selection interaction and the retained search state was released. +/// The cancelled variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class RemoteSessionMetadataValue +public partial class CatalogSelectionResultCancelled : CatalogSelectionResult { - /// Most recent working directory context. - [JsonPropertyName("context")] - public SessionContext? Context { get; set; } - - /// Host-supplied human description of what the session is doing right now ("running tests", "waiting for approval"). Optional in the protocol and absent on hosts that do not publish it, so never rely on it -- it enriches `hostStatus`, it does not replace it. - [JsonPropertyName("hostActivity")] - public string? HostActivity { get; set; } + /// + [JsonIgnore] + public override string Kind => "cancelled"; - /// Live status as the owning host reports it in its session listing, so a row for a session running elsewhere can show that it is running. Absent for hosts that publish no such status (the cloud task managers), which read as idle. - [JsonPropertyName("hostStatus")] - public RemoteSessionHostStatus? HostStatus { get; set; } - - /// Always true for remote sessions. - [JsonPropertyName("isRemote")] - public bool IsRemote { get; set; } - - /// Last-modified time as an ISO 8601 timestamp. - [JsonPropertyName("modifiedTime")] - public string ModifiedTime { get; set; } = string.Empty; - - /// Optional human-friendly name set via /rename. - [JsonPropertyName("name")] - public string? Name { get; set; } - - /// Pull request number associated with the session. - [JsonPropertyName("pullRequestNumber")] - public long? PullRequestNumber { get; set; } - - /// Backing remote session IDs (most recent first). - [JsonPropertyName("remoteSessionIds")] - public IList RemoteSessionIds { get => field ??= []; set; } - - /// GitHub repository the remote session belongs to. - [JsonPropertyName("repository")] - public RemoteSessionMetadataRepository Repository { get => field ??= new(); set; } - - /// Original remote resource identifier (task ID or PR node ID). - [JsonPropertyName("resourceId")] - public string? ResourceId { get; set; } - - /// Stable session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; - - /// Deadline (ISO 8601) at which a CLI remote session becomes stale without further heartbeats. - [JsonPropertyName("staleAt")] - public string? StaleAt { get; set; } - - /// Session creation time as an ISO 8601 timestamp. - [JsonPropertyName("startTime")] - public string StartTime { get; set; } = string.Empty; - - /// Server-side task state returned by GitHub. - [JsonPropertyName("state")] - public string? State { get; set; } - - /// Short summary of the session, when one has been derived. - [JsonPropertyName("summary")] - public string? Summary { get; set; } - - /// Whether the remote task originated from CCA or CLI `--remote`. - [JsonPropertyName("taskType")] - public RemoteSessionMetadataTaskType? TaskType { get; set; } + /// The search identifier privately bound to the released selection group. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(64)] + [JsonPropertyName("searchId")] + public required string SearchId { get; set; } } -/// `sessions.open` handoff progress update with step, status, and optional message. +/// The host declared that its live selection interaction timed out, and the retained search state was released. +/// The timed-out variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionsOpenProgress +public partial class CatalogSelectionResultTimedOut : CatalogSelectionResult { - /// Optional step message. - [JsonPropertyName("message")] - public string? Message { get; set; } - - /// Step status. - [JsonPropertyName("status")] - public SessionsOpenProgressStatus Status { get; set; } + /// + [JsonIgnore] + public override string Kind => "timed-out"; - /// Handoff step. - [JsonPropertyName("step")] - public SessionsOpenProgressStep Step { get; set; } + /// The search identifier privately bound to the released selection group. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(64)] + [JsonPropertyName("searchId")] + public required string SearchId { get; set; } } -/// Result of opening a session. +/// The selection reference was malformed or unknown. +/// The invalid variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionOpenResult +public partial class CatalogSelectionResultInvalid : CatalogSelectionResult { - /// Remote session metadata, present when status is `connected`. - [JsonPropertyName("metadata")] - public RemoteSessionMetadataValue? Metadata { get; set; } - - /// Handoff progress steps, present when status is `handed_off`. - [JsonPropertyName("progress")] - public IList? Progress { get; set; } - - /// Remote session ID, present when status is `connected`. - [JsonPropertyName("remoteSessionId")] - public string? RemoteSessionId { get; set; } + /// + [JsonIgnore] + public override string Kind => "invalid"; - /// Opened session ID. Omitted when status is `not_found`. - [JsonPropertyName("sessionId")] - public string? SessionId { get; set; } + /// Human-readable explanation safe to surface. Never contains the presented reference or private candidate state. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } +} - /// Startup prompts queued by user-level hook configs at session creation. Only populated when status is `created`; resumed sessions return an empty array. - [JsonPropertyName("startupPrompts")] - public IList? StartupPrompts { get; set; } +/// The runtime-enforced selection reference lifetime elapsed before the request arrived. +/// The stale variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class CatalogSelectionResultStale : CatalogSelectionResult +{ + /// + [JsonIgnore] + public override string Kind => "stale"; - /// Outcome of the open request. - [JsonPropertyName("status")] - public SessionsOpenStatus Status { get; set; } + /// Human-readable explanation safe to surface. Never contains the presented reference or private candidate state. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } } -/// Identifier and optional friendly name assigned to the newly forked session. +/// The selection group was already terminated or its pending host hand-off was already claimed. +/// The replayed variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionsForkResult +public partial class CatalogSelectionResultReplayed : CatalogSelectionResult { - /// Friendly name assigned to the forked session, if any. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// + [JsonIgnore] + public override string Kind => "replayed"; - /// The new forked session's ID. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Human-readable explanation safe to surface. Never contains the presented reference or private candidate state. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } } -/// Source session identifier to fork from, optional event-ID boundary, and optional friendly name for the new session. +/// The selection reference belongs to another runtime instance or session. +/// The foreign variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsForkRequest +public partial class CatalogSelectionResultForeign : CatalogSelectionResult { - /// Optional friendly name to assign to the forked session. - [JsonPropertyName("name")] - public string? Name { get; set; } - - /// Source session ID to fork from. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "foreign"; - /// Optional event ID boundary. When provided, the fork includes only events before this ID (exclusive). When omitted, all events are included. - [JsonPropertyName("toEventId")] - public string? ToEventId { get; set; } + /// Human-readable explanation safe to surface. Never contains the presented reference or private candidate state. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } } -/// Repository associated with the connected remote session. +/// The presented opaque handle was issued for another catalog operation. +/// The wrong-kind variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectedRemoteSessionMetadataRepository +public partial class CatalogSelectionResultWrongKind : CatalogSelectionResult { - /// Branch associated with the remote session. - [JsonPropertyName("branch")] - public string Branch { get; set; } = string.Empty; - - /// Repository name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "wrong-kind"; - /// Repository owner or organization login. - [JsonPropertyName("owner")] - public string Owner { get; set; } = string.Empty; + /// Human-readable explanation safe to surface. Never contains the presented reference or private candidate state. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } } -/// Metadata for a connected remote session. +/// The caller's protocol version or required capabilities cannot be honoured. Returned instead of a partial or ambiguous success. +/// The negotiation-refused variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectedRemoteSessionMetadata +public partial class CatalogSelectionResultNegotiationRefused : CatalogSelectionResult { - /// Neutral SDK discriminator for the connected remote session kind. - [JsonPropertyName("kind")] - public ConnectedRemoteSessionMetadataKind Kind { get; set; } - - /// Last session update time as an ISO 8601 string. - [JsonPropertyName("modifiedTime")] - public DateTimeOffset ModifiedTime { get; set; } + /// + [JsonIgnore] + public override string Kind => "negotiation-refused"; - /// Optional friendly session name. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } - /// Pull request number associated with the session. - [JsonPropertyName("pullRequestNumber")] - public long? PullRequestNumber { get; set; } + /// Lowest caller protocol version this runtime will serve. + [JsonPropertyName("minimumSupportedProtocolVersion")] + public required long MinimumSupportedProtocolVersion { get; set; } - /// Repository associated with the connected remote session. - [JsonPropertyName("repository")] - public ConnectedRemoteSessionMetadataRepository Repository { get => field ??= new(); set; } + /// Whether the version or the capability set was the problem. + [JsonPropertyName("reason")] + public required CatalogNegotiationRefusedReason Reason { get; set; } - /// Original remote resource identifier. - [JsonPropertyName("resourceId")] - public string? ResourceId { get; set; } + /// Protocol version of the runtime that refused the request. + [JsonPropertyName("runtimeProtocolVersion")] + public required long RuntimeProtocolVersion { get; set; } - /// SDK session ID for the connected remote session. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Capabilities this runtime can safely advertise to this caller. The complete five-capability protocol-3 legacy set is always present; every capability added after that baseline appears only when the caller required it, so an older closed-enum decoder can still consume a refusal. This list does not imply that every deployment has enabled every operation. + [JsonPropertyName("supportedCapabilities")] + public required IList SupportedCapabilities { get; set; } - /// Remote session staleness deadline as an ISO 8601 string. - [JsonPropertyName("staleAt")] - public DateTimeOffset? StaleAt { get; set; } + /// The subset of the caller's bounded extensible capability identifiers this runtime cannot honour. + [JsonPropertyName("unsupportedCapabilities")] + public required IList UnsupportedCapabilities { get; set; } +} - /// Session start time as an ISO 8601 string. - [JsonPropertyName("startTime")] - public DateTimeOffset StartTime { get; set; } +/// The request was rejected because a bounded field fell outside its permitted range or a required field was unusable. Pagination may also be rejected by the authority after a continuation request; repeat the search without page. +/// The invalid-request variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class CatalogSelectionResultInvalidRequest : CatalogSelectionResult +{ + /// + [JsonIgnore] + public override string Kind => "invalid-request"; - /// Remote session state returned by the backing service. - [JsonPropertyName("state")] - public string? State { get; set; } + /// Which request field was rejected. + [JsonPropertyName("field")] + public required CatalogInvalidRequestField Field { get; set; } - /// Optional session summary. - [JsonPropertyName("summary")] - public string? Summary { get; set; } + /// Human-readable explanation, safe to surface. Never echoes the offending value, nor a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } } -/// Remote session connection result. +/// The operation is not available on this runtime. Distinct from a network failure: nothing was attempted. +/// The unavailable variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class RemoteSessionConnectionResult +public partial class CatalogSelectionResultUnavailable : CatalogSelectionResult { - /// Metadata for a connected remote session. - [JsonPropertyName("metadata")] - public ConnectedRemoteSessionMetadata Metadata { get => field ??= new(); set; } + /// + [JsonIgnore] + public override string Kind => "unavailable"; - /// SDK session ID for the connected remote session. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Human-readable explanation, safe to surface. Never contains a query, URL, handle, or secret. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(1000)] + [JsonPropertyName("message")] + public required string Message { get; set; } + + /// Why the operation is unavailable. + [JsonPropertyName("reason")] + public required CatalogUnavailableReason Reason { get; set; } } -/// Remote session connection parameters. +/// Terminates one retained catalog selection group through an opaque reference previously returned by the model-safe search projection. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ConnectRemoteSessionParams +internal sealed class CatalogSelectionRequest { - /// Session ID to connect to. + /// Protocol version and capabilities the caller requires. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); set; } + + /// The terminal outcome declared by the caller. Timed-out means the host's live interaction deadline elapsed; a reference whose runtime TTL elapsed is rejected separately as stale. + [JsonPropertyName("outcome")] + public CatalogSelectionDecision Outcome { get; set; } + + /// Opaque runtime-instance scoped reference to one visible candidate. For a non-selected outcome, any candidate reference from the same search closes that search's retained group. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(256)] + [JsonPropertyName("selectionRef")] + public string SelectionRef { get; set; } = string.Empty; + + /// Locally owned root session whose retained search state is being resolved. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(256)] [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Local or remote session metadata entry. Narrow on `isRemote` to access source-specific fields. -/// Data type discriminated by isRemote. +/// Information about an installed plugin tracked in global state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SessionListEntry +public sealed class InstalledPluginInfo { - /// The boolean discriminator. - [JsonPropertyName("isRemote")] - public bool IsRemote { get; set; } - - /// Runtime client name that created/last resumed this session. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("clientName")] - public string? ClientName { get; set; } + /// Opaque, stable hash identifying a direct (non-marketplace) install source. Present only for direct repo / URL / local installs; absent for marketplace plugins. Same source yields the same id; distinct sources never collide. + [JsonPropertyName("directSourceId")] + public string? DirectSourceId { get; set; } - /// Pre-resolved working-directory context for session startup. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("context")] - public SessionContext? Context { get; set; } + /// Whether the plugin is currently enabled for new sessions. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } - /// Host-supplied human description of what the session is doing right now ("running tests", "waiting for approval"). Optional in the protocol and absent on hosts that do not publish it, so never rely on it -- it enriches `hostStatus`, it does not replace it. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("hostActivity")] - public string? HostActivity { get; set; } + /// Whether the managed desired plugin currently has an installed or live record. Set to false for a managed desired entry retained in the listing after installation or reconciliation failed. + [JsonPropertyName("installed")] + public bool? Installed { get; set; } - /// Live status as the owning host reports it in its session listing, so a row for a session running elsewhere can show that it is running. Absent for hosts that publish no such status (the cloud task managers), which read as idle. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("hostStatus")] - public RemoteSessionHostStatus? HostStatus { get; set; } + /// Absolute path of the marketplace directory a live plugin was resolved from. Present only on live, never-persisted records — a plugin belonging to a directory/local marketplace, which is loaded from its real directory on every pass instead of a copy under the installed-plugins cache. Its presence is what marks a listed plugin as live: such a plugin is always present on disk, so `enabled` is its only meaningful state and it is never "not installed". + [JsonPropertyName("installedFrom")] + public string? InstalledFrom { get; set; } - /// True for detached maintenance sessions that should be hidden from normal resume lists. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("isDetached")] - public bool? IsDetached { get; set; } + /// Whether enterprise managed settings control this plugin's enabled state. + [JsonPropertyName("managed")] + public bool? Managed { get; set; } - /// GitHub task ID, when this local session is bound to one. Only present for local sessions exported to remote control. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("mcTaskId")] - public string? McTaskId { get; set; } + /// The enabled state required by enterprise managed settings, when this plugin spec is managed. + [JsonPropertyName("managedDesiredEnabled")] + public bool? ManagedDesiredEnabled { get; set; } - /// Last-modified time of the session's persisted state, as ISO 8601. - [JsonPropertyName("modifiedTime")] - public required string ModifiedTime { get; set; } + /// Marketplace the plugin came from. Empty string ("") for direct repo / URL / local installs. + [JsonPropertyName("marketplace")] + public string Marketplace { get; set; } = string.Empty; - /// Optional human-friendly name set via /rename. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + /// Plugin name. [JsonPropertyName("name")] - public string? Name { get; set; } - - /// Pull request number associated with the session. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("pullRequestNumber")] - public long? PullRequestNumber { get; set; } - - /// Backing remote session IDs (most recent first). - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("remoteSessionIds")] - public IList? RemoteSessionIds { get; set; } - - /// GitHub repository the remote session belongs to. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("repository")] - public RemoteSessionMetadataRepository? Repository { get; set; } - - /// Original remote resource identifier (task ID or PR node ID). - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("resourceId")] - public string? ResourceId { get; set; } - - /// Stable session identifier. - [JsonPropertyName("sessionId")] - public required string SessionId { get; set; } - - /// Deadline (ISO 8601) at which a CLI remote session becomes stale without further heartbeats. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("staleAt")] - public string? StaleAt { get; set; } - - /// Session creation time as an ISO 8601 timestamp. - [JsonPropertyName("startTime")] - public required string StartTime { get; set; } - - /// Server-side task state returned by GitHub. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("state")] - public string? State { get; set; } + public string Name { get; set; } = string.Empty; - /// Short summary of the session, when one has been derived. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("summary")] - public string? Summary { get; set; } + /// Runtime-reported plugin provenance. Currently set to "builtin" only for plugins registered through the trusted host built-in boundary; absent for installed, marketplace, direct, and live plugins. + [JsonPropertyName("source")] + public string? Source { get; set; } - /// Whether the remote task originated from CCA or CLI `--remote`. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("taskType")] - public RemoteSessionMetadataTaskType? TaskType { get; set; } + /// Installed version (when reported by the plugin manifest). + [JsonPropertyName("version")] + public string? Version { get; set; } } -/// Sessions matching the filter, ordered most-recently-modified first. +/// Plugins installed in user/global state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionList +public sealed class PluginListResult { - /// Sessions ordered most-recently-modified first. Discriminated by `isRemote`. - [JsonPropertyName("sessions")] - public IList Sessions { get => field ??= []; set; } + /// Installed plugins. + [JsonPropertyName("plugins")] + public IList Plugins { get => field ??= []; set; } } -/// Optional filter applied to the returned sessions. +/// Result of installing a plugin. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionListFilter +public sealed class PluginInstallResult { - /// Match sessions whose context.branch equals this value. - [JsonPropertyName("branch")] - public string? Branch { get; set; } + /// Set when the install path is deprecated (e.g. direct repo / URL / local installs). Callers should surface this to end users. + [JsonPropertyName("deprecationWarning")] + public string? DeprecationWarning { get; set; } - /// Match sessions whose context.cwd equals this value. - [JsonPropertyName("cwd")] - public string? Cwd { get; set; } + /// The newly installed plugin's metadata. + [JsonPropertyName("plugin")] + public InstalledPluginInfo Plugin { get => field ??= new(); set; } - /// Match sessions whose context.gitRoot equals this value. - [JsonPropertyName("gitRoot")] - public string? GitRoot { get; set; } + /// Optional post-install message provided by the plugin (e.g. setup instructions). + [JsonPropertyName("postInstallMessage")] + public string? PostInstallMessage { get; set; } - /// Match sessions whose context.repository equals this value. - [JsonPropertyName("repository")] - public string? Repository { get; set; } + /// Number of skills discovered and installed from the plugin. + [JsonPropertyName("skillsInstalled")] + public long SkillsInstalled { get; set; } + + /// Where the completed plugin tree was staged before atomic promotion. + [JsonPropertyName("stagingMode")] + public PluginInstallStagingMode? StagingMode { get; set; } } -/// Optional source filter, metadata-load limit, and context filter applied to the returned sessions. +/// Plugin source and optional working directory for relative-path resolution. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsListRequest +internal sealed class PluginsInstallRequest { - /// Optional filter applied to the returned sessions. - [JsonPropertyName("filter")] - public SessionListFilter? Filter { get; set; } - - /// When true, include detached maintenance sessions. Defaults to false for user-facing session lists. - [JsonPropertyName("includeDetached")] - public bool? IncludeDetached { get; set; } - - /// When provided, only the first N local sessions (sorted by modification time, newest first) load full metadata; remaining sessions return basic info only. Use 0 to return only basic info for every local session. Has no effect on remote entries (which always carry their full shape). - [JsonPropertyName("metadataLimit")] - public long? MetadataLimit { get; set; } - - /// Which session sources to include. Defaults to `local` for backward compatibility. + /// Plugin install spec. Accepts the same forms as the CLI: "plugin@marketplace" (marketplace install), "owner/repo" or "owner/repo:subpath" (GitHub direct), an http/https/ssh URL, or a local path. Direct (non-marketplace) installs are deprecated and will produce a deprecationWarning in the result. [JsonPropertyName("source")] - public SessionSource? Source { get; set; } + public string Source { get; set; } = string.Empty; - /// Only meaningful when `source` includes remote. When true, propagates errors from the remote service instead of silently returning an empty remote list. Defaults to false. - [JsonPropertyName("throwOnError")] - public bool? ThrowOnError { get; set; } + /// Working directory used to resolve relative local paths in `source`. Defaults to the server's current working directory. + [JsonPropertyName("workingDirectory")] + public string? WorkingDirectory { get; set; } } -/// Persisted local session metadata, including identifiers, timestamps, summary/name, client, context, detached state, and task ID. +/// Name (or spec) of the plugin to uninstall. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class LocalSessionMetadataValue +internal sealed class PluginsUninstallRequest { - /// Runtime client name that created/last resumed this session. - [JsonPropertyName("clientName")] - public string? ClientName { get; set; } + /// Stable source identity for a direct (non-marketplace) install. Disambiguates uninstall when multiple installed plugins share the same name. + [JsonPropertyName("directSourceId")] + public string? DirectSourceId { get; set; } - /// Pre-resolved working-directory context for session startup. - [JsonPropertyName("context")] - public SessionContext? Context { get; set; } - - /// True for detached maintenance sessions that should be hidden from normal resume lists. - [JsonPropertyName("isDetached")] - public bool? IsDetached { get; set; } - - /// Always false for local sessions. - [JsonPropertyName("isRemote")] - public bool IsRemote { get; set; } - - /// GitHub task ID, when this local session is bound to one. Only present for local sessions exported to remote control. - [JsonPropertyName("mcTaskId")] - public string? McTaskId { get; set; } - - /// Last-modified time of the session's persisted state, as ISO 8601. - [JsonPropertyName("modifiedTime")] - public string ModifiedTime { get; set; } = string.Empty; - - /// Optional human-friendly name set via /rename. + /// Plugin name or "plugin@marketplace" spec to uninstall. When ambiguous, prefer the fully-qualified spec. [JsonPropertyName("name")] - public string? Name { get; set; } - - /// Stable session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; - - /// Session creation time as an ISO 8601 timestamp. - [JsonPropertyName("startTime")] - public string StartTime { get; set; } = string.Empty; - - /// Short summary of the session, when one has been derived. - [JsonPropertyName("summary")] - public string? Summary { get; set; } + public string Name { get; set; } = string.Empty; } -/// Persisted local session metadata when the session exists. +/// Result of updating a single plugin. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsGetMetadataResult +public sealed class PluginUpdateResult { - /// Local session metadata, omitted when the session does not exist. - [JsonPropertyName("session")] - public LocalSessionMetadataValue? Session { get; set; } -} + /// Version after the update, when reported by the plugin manifest. + [JsonPropertyName("newVersion")] + public string? NewVersion { get; set; } -/// Session ID whose persisted metadata should be read. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsGetMetadataRequest -{ - /// Session ID to inspect. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Version that was previously installed, when available. + [JsonPropertyName("previousVersion")] + public string? PreviousVersion { get; set; } + + /// Number of skills discovered and installed after the update. + [JsonPropertyName("skillsInstalled")] + public long SkillsInstalled { get; set; } } -/// Client metadata outcome for one requested local session. -/// Polymorphic base type discriminated by status. +/// Name (or spec) of the plugin to update. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "status", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(SessionsClientMetadataEntryOk), "ok")] -[JsonDerivedType(typeof(SessionsClientMetadataEntryNotFound), "notFound")] -[JsonDerivedType(typeof(SessionsClientMetadataEntryCorrupt), "corrupt")] -[JsonDerivedType(typeof(SessionsClientMetadataEntryUnsupportedVersion), "unsupportedVersion")] -[JsonDerivedType(typeof(SessionsClientMetadataEntryUnavailable), "unavailable")] -public partial class SessionsClientMetadataEntry +internal sealed class PluginsUpdateRequest { - /// The type discriminator. - [JsonPropertyName("status")] - public virtual string Status { get; set; } = string.Empty; + /// Plugin name or "plugin@marketplace" spec to update. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } - -/// The ok variant of . +/// Per-plugin result from updating all plugins, with versions, skills installed, success flag, and optional error. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SessionsClientMetadataEntryOk : SessionsClientMetadataEntry +public sealed class PluginUpdateAllEntry { - /// - [JsonIgnore] - public override string Status => "ok"; + /// Error message (failure only). + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Validated client metadata, possibly empty or projected to requested keys. - [JsonPropertyName("metadata")] - public required IDictionary Metadata { get; set; } + /// Marketplace the plugin came from. Empty string ("") for direct installs. + [JsonPropertyName("marketplace")] + public string Marketplace { get; set; } = string.Empty; - /// Requested session ID. - [JsonPropertyName("sessionId")] - public required string SessionId { get; set; } -} + /// Plugin name that was updated. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; -/// The notFound variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SessionsClientMetadataEntryNotFound : SessionsClientMetadataEntry -{ - /// - [JsonIgnore] - public override string Status => "notFound"; + /// Version after the update, when available. + [JsonPropertyName("newVersion")] + public string? NewVersion { get; set; } - /// Requested session ID. - [JsonPropertyName("sessionId")] - public required string SessionId { get; set; } -} + /// Previously installed version, when available. + [JsonPropertyName("previousVersion")] + public string? PreviousVersion { get; set; } -/// The corrupt variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SessionsClientMetadataEntryCorrupt : SessionsClientMetadataEntry -{ - /// - [JsonIgnore] - public override string Status => "corrupt"; + /// Number of skills installed after the update (success only). + [JsonPropertyName("skillsInstalled")] + public long? SkillsInstalled { get; set; } - /// Requested session ID. - [JsonPropertyName("sessionId")] - public required string SessionId { get; set; } + /// Whether the update succeeded for this plugin. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// The unsupportedVersion variant of . +/// Result of updating all installed plugins. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SessionsClientMetadataEntryUnsupportedVersion : SessionsClientMetadataEntry +public sealed class PluginUpdateAllResult { - /// - [JsonIgnore] - public override string Status => "unsupportedVersion"; - - /// Requested session ID. - [JsonPropertyName("sessionId")] - public required string SessionId { get; set; } + /// Per-plugin update results in deterministic order. + [JsonPropertyName("results")] + public IList Results { get => field ??= []; set; } } -/// The unavailable variant of . +/// Plugin names (or specs) to enable, plus the optional working directory the repository-controlled guard is evaluated against. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SessionsClientMetadataEntryUnavailable : SessionsClientMetadataEntry +internal sealed class PluginsEnableRequest { - /// - [JsonIgnore] - public override string Status => "unavailable"; - - /// Filesystem or provider error code. Clients should not assume every provider uses operating-system error codes. - [JsonPropertyName("code")] - public required string Code { get; set; } - - /// Human-readable diagnostic message. Not stable for programmatic matching. - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Plugin names or "plugin@marketplace" specs to enable. Unknown names are ignored. Non-marketplace direct installs are always enabled and cannot be toggled via this API. + [JsonPropertyName("names")] + public IList Names { get => field ??= []; set; } - /// Requested session ID. - [JsonPropertyName("sessionId")] - public required string SessionId { get; set; } + /// Working directory whose repository `enabledPlugins` overlay decides whether this mutation is repository-controlled. Hosts that serve sessions across several repositories (the SDK server) should pass the session's directory; otherwise the guard is evaluated against the server process's own working directory, which may belong to a different repository. Defaults to the server's current working directory. + [JsonPropertyName("workingDirectory")] + public string? WorkingDirectory { get; set; } } -/// Bounded batch request for client-owned metadata from persisted local sessions. +/// Plugin names (or specs) to disable, plus the optional working directory the repository-controlled guard is evaluated against. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsGetClientMetadataRequest +internal sealed class PluginsDisableRequest { - /// Case-sensitive keys to project from each valid bag. Each key must be non-empty, at most 256 UTF-8 bytes, and outside the reserved `copilot/` and `github/` namespaces. Omit to return every entry. - [JsonPropertyName("keys")] - public IList? Keys { get; set; } + /// Plugin names or "plugin@marketplace" specs to disable. Unknown names are ignored. Non-marketplace direct installs cannot be disabled via this API; uninstall them instead. Plugin-owned MCP servers are stopped in active sessions immediately; other plugin contributions remain available until each session reloads plugins. + [JsonPropertyName("names")] + public IList Names { get => field ??= []; set; } - /// Session IDs to inspect. Results preserve this order. - [JsonPropertyName("sessionIds")] - public IList SessionIds { get => field ??= []; set; } + /// Working directory whose repository `enabledPlugins` overlay decides whether this mutation is repository-controlled. Hosts that serve sessions across several repositories (the SDK server) should pass the session's directory; otherwise the guard is evaluated against the server process's own working directory, which may belong to a different repository. Defaults to the server's current working directory. + [JsonPropertyName("workingDirectory")] + public string? WorkingDirectory { get; set; } } -/// Batch of session events returned by a read, with cursor and continuation metadata. +/// Trusted built-in plugin directories to use for this runtime process. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class EventsReadResult +internal sealed class PluginsBuiltinSetRequest { - /// Opaque cursor for the next read. Pass back unchanged in the next read.cursor to continue from where this read left off. Always present, even when no events were returned. For a backward read this cursor pages toward OLDER events; keep passing `direction: backward` with it (the cursor is also self-describing, so backward paging continues correctly). - [JsonPropertyName("cursor")] - public string Cursor { get; set; } = string.Empty; - - /// Cursor status: 'ok' means the cursor was applied successfully. For session.eventLog.read, 'expired' means the cursor referred to an event that no longer exists in active history and the read fell back to a boundary of the remaining history: the beginning for a forward read or the newest window for a backward read. That fallback may overlap already rendered events, so active-session consumers should reset, rebase, or deduplicate before continuing. sessions.readPersistedEvents has stricter snapshot semantics: 'expired' returns an empty terminal page and never switches to a replacement journal generation. Other persisted-read I/O failures are RPC errors with diagnostics, not cursor expiry. - [JsonPropertyName("cursorStatus")] - public EventsCursorStatus CursorStatus { get; set; } - - /// Session events for this batch, merged into a single stream in creation order: durable (persisted) events and ephemeral events interleave exactly as they were emitted. Set `includeEphemeral: false` to receive only durable events. Ephemeral events are never replayable once pruned from the in-memory ring, so a consumer that needs them should keep reading with a non-zero `waitMs`. For a backward (tail-first) read, the returned window contains persisted events only, still in chronological (oldest-to-newest) append order. - [JsonPropertyName("events")] - public IList Events { get => field ??= []; set; } - - /// True when more events are available in the read's direction. For a backward read, true means older persisted events remain before the returned window. A persisted-event page may contain fewer than `max` events because of its byte budget while still reporting hasMore true; continue according to this flag rather than the event count. - [JsonPropertyName("hasMore")] - public bool HasMore { get; set; } + /// Complete replacement set of trusted built-in plugin directories. Every entry must be an absolute local filesystem path no longer than 4096 characters. + [JsonPropertyName("paths")] + public IList Paths { get => field ??= []; set; } } -/// Pagination options for reading an inactive or active local session's persisted event journal. +/// Registered marketplace summary. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsReadPersistedEventsRequest +public sealed class MarketplaceInfo { - /// Opaque, process-local, single-use cursor returned by the previous persisted-event read. Omit on the first call and issue continuations sequentially; reusing the same cursor returns an expired terminal page. - [JsonPropertyName("cursor")] - public string? Cursor { get; set; } + /// Whether the managed marketplace currently resolved into the runtime marketplace registry. Set to false when the desired managed entry is retained for governance visibility after loading or reconciliation failed. + [JsonPropertyName("available")] + public bool? Available { get; set; } - /// Direction to page through persisted history. Forward starts at the beginning; backward starts with the newest events. Events in each page remain chronological. This selects the initial read only; a continuation always uses the direction bound into its cursor. - [JsonPropertyName("direction")] - public EventsReadDirection? Direction { get; set; } + /// True when this is a default marketplace shipped with the runtime. Defaults are not removable. + [JsonPropertyName("isDefault")] + public bool? IsDefault { get; set; } - /// Maximum number of events to return in this batch (1–1000, default 200). Pages may contain fewer events to keep the serialized event array within a soft 1 MiB budget including resolved binary assets; one oversized event is returned alone to guarantee progress. - [JsonPropertyName("max")] - public long? Max { get; set; } + /// Whether enterprise managed settings provide and control this marketplace entry. + [JsonPropertyName("managed")] + public bool? Managed { get; set; } - /// Session ID whose persisted event journal should be read. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Marketplace name (matches the @marketplace suffix in plugin specs). + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Human-readable description of where the marketplace data is fetched from (e.g. "GitHub: owner/repo"). + [JsonPropertyName("source")] + public string Source { get; set; } = string.Empty; } -/// Recent local session IDs that contain user-visible history. +/// All registered marketplaces, including built-in defaults. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsListNonEmptySessionIdsResult +public sealed class MarketplaceListResult { - /// Session IDs ordered newest-first. - [JsonPropertyName("sessionIds")] - public IList SessionIds { get => field ??= []; set; } + /// Registered marketplaces. + [JsonPropertyName("marketplaces")] + public IList Marketplaces { get => field ??= []; set; } } -/// Limit for non-empty local session IDs. +/// Result of registering a new marketplace. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsListNonEmptySessionIdsRequest +public sealed class MarketplaceAddResult { - /// Maximum number of session IDs to return. - [JsonPropertyName("limit")] - public long? Limit { get; set; } + /// Final name of the marketplace as resolved from its manifest. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// ID of the local session bound to the given GitHub task, or omitted when none. +/// Marketplace source and optional working directory for relative-path resolution. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionsFindByTaskIDResult +internal sealed class PluginsMarketplacesAddRequest { - /// Omitted when no local session is bound to that GitHub task. - [JsonPropertyName("sessionId")] - public string? SessionId { get; set; } + /// Marketplace source. Accepts the same forms as the CLI: "owner/repo" or "owner/repo#ref" (GitHub), an http/https/ssh URL (optionally with #ref), a git scp-style URL (user@host:path), or a local path. The marketplace's own name (from its manifest) is used as the registration key. + [JsonPropertyName("source")] + public string Source { get; set; } = string.Empty; + + /// Working directory used to resolve relative local paths in `source`. Defaults to the server's current working directory. + [JsonPropertyName("workingDirectory")] + public string? WorkingDirectory { get; set; } } -/// GitHub task ID to look up. +/// Outcome of the remove attempt, including dependent-plugin info when applicable. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsFindByTaskIDRequest +public sealed class MarketplaceRemoveResult { - /// GitHub task ID to look up. - [JsonPropertyName("taskId")] - public string TaskId { get; set; } = string.Empty; + /// Names of installed plugins that prevented removal. Populated only when `removed=false`. + [JsonPropertyName("dependentPlugins")] + public IList? DependentPlugins { get; set; } + + /// True when the marketplace was actually removed. False when removal was skipped because the marketplace has dependent plugins and `force` was not set. + [JsonPropertyName("removed")] + public bool Removed { get; set; } } -/// Session ID matching the prefix, omitted when no unique match exists. +/// Name of the marketplace to remove and an optional force flag. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionsFindByPrefixResult +internal sealed class PluginsMarketplacesRemoveRequest { - /// Omitted when no unique session matches the prefix (no match or ambiguous). - [JsonPropertyName("sessionId")] - public string? SessionId { get; set; } + /// When true, also uninstall every plugin sourced from this marketplace. When false (default), removal is a no-op if any plugin from this marketplace is installed and the dependent plugin names are returned in the result. + [JsonPropertyName("force")] + public bool? Force { get; set; } + + /// Marketplace name to remove. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// UUID prefix to resolve to a unique session ID. +/// Plugin entry advertised by a marketplace. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsFindByPrefixRequest +public sealed class MarketplacePluginInfo { - /// UUID prefix (>=7 hex chars, <36 chars). Returns the unique session ID, or undefined when there is no match or the prefix matches multiple sessions. - [JsonPropertyName("prefix")] - public string Prefix { get; set; } = string.Empty; + /// Short description from the marketplace catalog, when present. + [JsonPropertyName("description")] + public string? Description { get; set; } + + /// Plugin name as listed in the marketplace catalog. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// Most-relevant session ID for the supplied context, or omitted when no sessions exist. +/// Plugins advertised by the marketplace. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionsGetLastForContextResult +public sealed class MarketplaceBrowseResult { - /// Most-relevant session ID for the supplied context, or omitted when no sessions exist. - [JsonPropertyName("sessionId")] - public string? SessionId { get; set; } + /// Plugins advertised by the marketplace. + [JsonPropertyName("plugins")] + public IList Plugins { get => field ??= []; set; } } -/// Optional working-directory context used to score session relevance. +/// Name of the marketplace whose plugin catalog to fetch. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsGetLastForContextRequest +internal sealed class PluginsMarketplacesBrowseRequest { - /// Optional working-directory context used to score session relevance. When omitted the most-recently-modified session wins. - [JsonPropertyName("context")] - public SessionContext? Context { get; set; } + /// Marketplace name to browse. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// Absolute path to the session's events.jsonl file on disk. +/// Per-marketplace refresh result, including marketplace name, success flag, and optional failure error. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsGetEventFilePathResult +public sealed class MarketplaceRefreshEntry { - /// Absolute path to the session's events.jsonl file. - [JsonPropertyName("filePath")] - public string FilePath { get; set; } = string.Empty; + /// Error message (failure only). + [JsonPropertyName("error")] + public string? Error { get; set; } + + /// Marketplace name that was refreshed. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Whether the refresh succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Session ID whose event-log file path to compute. +/// Result of refreshing one or more marketplace catalogs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsGetEventFilePathRequest +public sealed class MarketplaceRefreshResult { - /// Session ID whose event-log file path to compute. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Per-marketplace refresh results in deterministic order. + [JsonPropertyName("results")] + public IList Results { get => field ??= []; set; } } -/// Map of sessionId -> on-disk size in bytes for each session's workspace directory. +/// RPC data type for PluginsMarketplacesRefresh operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionSizes +internal sealed class PluginsMarketplacesRefreshRequest { - /// Map of sessionId -> on-disk size in bytes for the session's workspace directory. - [JsonPropertyName("sizes")] - public IDictionary Sizes { get => field ??= new Dictionary(); set; } + /// Marketplace name to refresh. When omitted, every registered marketplace is refreshed. + [JsonPropertyName("name")] + public string? Name { get; set; } } -/// Session IDs from the input set that are currently in use by another process. +/// Agent metadata, including identifiers, display details, source, tools, model, models, reasoning effort, MCP servers, skills, and file path. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionsCheckInUseResult +public sealed class AgentInfo { - /// Session IDs from the input set that are currently held by another running process via an alive lock file. - [JsonPropertyName("inUse")] - public IList InUse { get => field ??= []; set; } + /// Description of the agent's purpose. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; + + /// Whether model-driven invocation is disabled for this agent. + [JsonPropertyName("disableModelInvocation")] + public bool? DisableModelInvocation { get; set; } + + /// Human-readable display name. + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } = string.Empty; + + /// Declarative hook actions keyed by event name, executed only while this agent is active. + [JsonPropertyName("hooks")] + public IDictionary>? Hooks { get; set; } + + /// Stable identifier for selection. For most agents this is the same as `name`; for plugin/builtin agents it may differ. Always populated; defaults to `name` when no distinct id was assigned. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// MCP server configurations attached to this agent, keyed by server name. Server config shape mirrors the MCP `mcpServers` schema. + [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] + [JsonPropertyName("mcpServers")] + public IDictionary? McpServers { get; set; } + + /// Authored preferred model id for this agent. Runtime model selection may choose a different model; omitted means no authored preference. + [JsonPropertyName("model")] + public string? Model { get; set; } + + /// Whether authored models are preferences or required constraints. + [JsonPropertyName("modelPolicy")] + public AgentModelPolicy? ModelPolicy { get; set; } + + /// Authored preferred model ids for this agent, in priority order. Runtime model selection chooses the first available model; omitted means no authored preference. + [JsonPropertyName("models")] + public IList? Models { get; set; } + + /// Name of the agent. Use `id` as the stable selection identifier. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Absolute local file path of the agent definition. Only set for file-based agents loaded from disk; remote agents do not have a path. + [JsonPropertyName("path")] + public string? Path { get; set; } + + /// Authored base prompt for the agent. Runtime prompt assembly may add dynamic context at invocation time. Omitted from `session.agent.list` unless `includePrompt` is true. + [JsonPropertyName("prompt")] + public string? Prompt { get; set; } + + /// Authored reasoning effort for this agent. Applied on selection to models that support it; omitted means no authored preference. + [JsonPropertyName("reasoningEffort")] + public string? ReasoningEffort { get; set; } + + /// Skill names preloaded into this agent's context. Omitted means none. + [JsonPropertyName("skills")] + public IList? Skills { get; set; } + + /// Where the agent definition was loaded from. + [JsonPropertyName("source")] + public AgentInfoSource? Source { get; set; } + + /// Allowed tool names for this agent. Empty array means none; omitted means inherit defaults. + [JsonPropertyName("tools")] + public IList? Tools { get; set; } + + /// Whether the agent can be selected directly by the user. Agents marked `false` are subagent-only. + [JsonPropertyName("userInvocable")] + public bool? UserInvocable { get; set; } } -/// Session IDs to test for live in-use locks. +/// Agents discovered across user, project, plugin, and remote sources. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsCheckInUseRequest +public sealed class ServerAgentList { - /// Session IDs to test for live in-use locks. - [JsonPropertyName("sessionIds")] - public IList SessionIds { get => field ??= []; set; } + /// All discovered agents across all sources. + [JsonPropertyName("agents")] + public IList Agents { get => field ??= []; set; } } -/// The session's persisted remote-steerable flag, or omitted when no value has been persisted. +/// Optional project paths to include in agent discovery. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsGetPersistedRemoteSteerableResult +public sealed class AgentsDiscoverRequest { - /// The session's persisted remote-steerable flag if recorded; omitted when no value has been persisted. - [JsonPropertyName("remoteSteerable")] - public bool? RemoteSteerable { get; set; } + /// Explicit discovery-only agent roots, separate from customAgents and filesystem grants. Available without ambient discovery, subject to managed/source disables. Relative paths resolve against the request's effective working directory when supplied. Null and omission are equivalent; an empty array contributes no explicit roots. + [JsonPropertyName("agentDirectories")] + public IList? AgentDirectories { get; set; } + + /// When true, omit the host's agents (the user-level agent directory and all plugin agents), leaving only project and remote agents. For multitenant deployments. + [JsonPropertyName("excludeHostAgents")] + public bool? ExcludeHostAgents { get; set; } + + /// Optional list of project directory paths to scan for project-scoped agents. When omitted or empty, only user/plugin/remote-independent agents are returned (no project scan). + [JsonPropertyName("projectPaths")] + public IList? ProjectPaths { get; set; } } -/// Session ID to look up the persisted remote-steerable flag for. +/// Canonical directory where custom agents can be discovered or created, with scope, preference, and optional project path. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsGetPersistedRemoteSteerableRequest +public sealed class AgentDiscoveryPath { - /// Session ID to look up the persisted remote-steerable flag for. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Absolute path of the search/create directory (may not exist on disk yet). + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + + /// Whether this is the canonical directory to create a new agent in its tier. At most one entry per tier is preferred. + [JsonPropertyName("preferredForCreation")] + public bool PreferredForCreation { get; set; } + + /// The input project path this directory was derived from (only for project scope). + [JsonPropertyName("projectPath")] + public string? ProjectPath { get; set; } + + /// Which tier this directory belongs to. + [JsonPropertyName("scope")] + public AgentDiscoveryPathScope Scope { get; set; } } -/// 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. +/// Canonical locations where custom agents can be created so the runtime will recognize them. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionsCloseResult +public sealed class AgentDiscoveryPathList { + /// Canonical agent create/discovery directories, in priority order. + [JsonPropertyName("paths")] + public IList Paths { get => field ??= []; set; } } -/// Session ID to close. +/// Optional project paths to include when enumerating agent discovery directories. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsCloseRequest +public sealed class AgentsGetDiscoveryPathsRequest { - /// Session ID to close. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Explicit discovery-only agent roots to enumerate, including configured missing directories. Relative paths resolve against the request's effective working directory when supplied. These are not filesystem grants or preferred creation targets. Null and omission are equivalent. + [JsonPropertyName("agentDirectories")] + public IList? AgentDirectories { get; set; } -/// Map of sessionId -> bytes freed by removing the session's workspace directory. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionBulkDeleteResult -{ - /// Map of sessionId -> bytes freed by removing the session's workspace directory. Sessions whose deletion failed are omitted from this map (failures are logged on the server but not surfaced per-id; check the map for absent IDs to detect them). - [JsonPropertyName("freedBytes")] - public IDictionary FreedBytes { get => field ??= new Dictionary(); set; } + /// When true, omit the host's user-level agent directory, leaving only project directories. For multitenant deployments (mirrors `discover`'s `excludeHostAgents`). + [JsonPropertyName("excludeHostAgents")] + public bool? ExcludeHostAgents { get; set; } + + /// Optional list of project directory paths. When omitted or empty, only the user-level directory is returned. + [JsonPropertyName("projectPaths")] + public IList? ProjectPaths { get; set; } } -/// Session IDs to close, deactivate, and delete from disk. +/// The agents this runtime ships, named so a consumer can tell them apart from authored ones. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsBulkDeleteRequest +internal sealed class AgentsGetBuiltinsResult { - /// Session IDs to close, deactivate, and delete from disk. - [JsonPropertyName("sessionIds")] - public IList SessionIds { get => field ??= []; set; } + /// The subset of `names` a user is allowed to turn off. A shipped agent outside this list is always active and a client should not offer a toggle for it. + [JsonPropertyName("disableableNames")] + public IList DisableableNames { get => field ??= []; set; } + + /// Every agent name this runtime ships. + [JsonPropertyName("names")] + public IList Names { get => field ??= []; set; } + + /// The subset of `names` defined by a shipped YAML definition. The remainder are special-cased in code and have no definition to load. + [JsonPropertyName("yamlBasedNames")] + public IList YamlBasedNames { get => field ??= []; set; } } -/// Session ID to delete from disk. +/// A shipped agent, named and described. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsDeleteRequest +internal sealed class BuiltinAgentSummary { - /// Session ID to delete. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// One-line description of what the agent does. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; - /// Internal resolved session directory path to delete. - [JsonPropertyName("sessionPath")] - public string? SessionPath { get; set; } + /// The agent name, as it appears in `getBuiltins`. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// Outcome of the prune operation: deleted IDs, dry-run candidates, skipped IDs, total bytes freed, and the dry-run flag. +/// The shipped agents available under the requested flags. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionPruneResult +internal sealed class AgentsGetAvailableBuiltinsResult { - /// Session IDs that would be deleted in dry-run mode (always empty otherwise). - [JsonPropertyName("candidates")] - public IList Candidates { get => field ??= []; set; } - - /// Session IDs that were deleted (always empty in dry-run mode). - [JsonPropertyName("deleted")] - public IList Deleted { get => field ??= []; set; } - - /// True when no deletions were actually performed. - [JsonPropertyName("dryRun")] - public bool DryRun { get; set; } - - /// Total bytes freed (actual when not dry-run, projected when dry-run). - [JsonPropertyName("freedBytes")] - public long FreedBytes { get; set; } - - /// Session IDs that were skipped (e.g., named sessions). - [JsonPropertyName("skipped")] - public IList Skipped { get => field ??= []; set; } + /// Available shipped agents, in the runtime's own order. + [JsonPropertyName("agents")] + public IList Agents { get => field ??= []; set; } } -/// Age threshold and optional flags controlling which old sessions are pruned (or simulated when dryRun is true). +/// The feature flags to evaluate shipped agents against. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsPruneOldRequest +internal sealed class AgentsGetAvailableBuiltinsRequest { - /// When true, only report what would be deleted without performing any deletion. - [JsonPropertyName("dryRun")] - public bool? DryRun { get; set; } - - /// Session IDs that should never be considered for pruning. - [JsonPropertyName("excludeSessionIds")] - public IList? ExcludeSessionIds { get; set; } + /// The surface asking, which gates agents that only apply to one client. Omit or pass null to apply no client filter. + [JsonPropertyName("context")] + public string? Context { get; set; } - /// When true, named sessions (set via /rename) are also eligible for pruning. - [JsonPropertyName("includeNamed")] - public bool? IncludeNamed { get; set; } + /// Feature flag values keyed by name, evaluated with the runtime's truthiness rules. Omit or pass null for no flags. + [JsonPropertyName("featureFlags")] + public IDictionary? FeatureFlags { get; set; } - /// Delete sessions whose modifiedTime is at least this many days old. - [JsonPropertyName("olderThanDays")] - public long OlderThanDays { get; set; } + /// Flag overrides keyed by name. A null entry uses the corresponding base flag; false explicitly disables it. Omit or pass null for no overrides. + [JsonPropertyName("overrides")] + public IDictionary? Overrides { get; set; } } -/// Flush a session's pending events to disk. No-op when no writer exists for the session (e.g., already closed). +/// One shipped agent's definition. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionsSaveResult +internal sealed class AgentsGetBuiltinDefinitionResult { + /// The agent's definition, serialized as JSON. It carries the authored keys plus the runtime's projected `__nativeCustomAgent` view of the same agent. It is a string rather than an object because the runtime parses it with the agent schema's tolerant shape, which accepts keys this contract does not name. + [JsonPropertyName("definitionJson")] + public string DefinitionJson { get; set; } = string.Empty; } -/// Session ID whose pending events should be flushed to disk. +/// The shipped agent whose definition to load. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsSaveRequest +internal sealed class AgentsGetBuiltinDefinitionRequest { - /// Session ID whose pending events should be flushed to disk. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// The agent name, which must be one of `getBuiltins`'s `yamlBasedNames`. A name outside that list is special-cased in code and has no definition, and is reported as an error rather than as an empty definition. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// 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. +/// One shipped agent, projected for a listing. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionsReleaseLockResult +internal sealed class AgentsGetBuiltinListingDefinitionResult { + /// The agent projected as a custom agent, serialized as JSON. It is a string rather than an object for the same reason as `getBuiltinDefinition`: the runtime parses the underlying definition with the agent schema's tolerant shape, which accepts keys this contract does not name. + [JsonPropertyName("definitionJson")] + public string DefinitionJson { get; set; } = string.Empty; } -/// Session ID whose in-use lock should be released. +/// The shipped agent whose listing entry to load. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsReleaseLockRequest +internal sealed class AgentsGetBuiltinListingDefinitionRequest { - /// Session ID whose in-use lock should be released. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// The agent name, taken from `getAvailableBuiltins`. Unlike `getBuiltinDefinition`, the agent that `getBuiltins` reports as special-cased rather than YAML-based is answered here too, from its in-code definition. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// The enriched metadata records, with summary and context fields backfilled where available. Sessions confirmed empty and unnamed are omitted. +/// The model to switch to, and the warning to show when the agent's preference could not be met. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionEnrichMetadataResult +internal sealed class AgentsCustomAgentInitialModelDecisionResult { - /// Enriched records, with summary and context backfilled. Sessions confirmed empty and unnamed may be omitted. - [JsonPropertyName("sessions")] - public IList Sessions { get => field ??= []; set; } -} + /// The reasoning effort attached to the selected model preference. Absent when that preference does not specify an effort. + [JsonPropertyName("reasoningEffort")] + public string? ReasoningEffort { get; set; } -/// Session metadata records to enrich with summary and context information. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsEnrichMetadataRequest -{ - /// Session metadata records to enrich. Records that already have summary and context are returned unchanged. - [JsonPropertyName("sessions")] - public IList Sessions { get => field ??= []; set; } + /// The first available model that matches the agent's preferences. Absent when none of the requested models is available. + [JsonPropertyName("targetModel")] + public string? TargetModel { get; set; } + + /// What to tell the user about an unmet preference. Absent when the preference was met. A warning with no `targetModel` means the agent's models are all unavailable. + [JsonPropertyName("warning")] + public string? Warning { get; set; } } -/// The workspace record that was written. +/// The models a custom agent asks for, and the models actually available. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsCreateWorkspaceResult +internal sealed class AgentsCustomAgentInitialModelDecisionParams { - /// The created workspace record, as JSON. - [JsonPropertyName("workspaceJson")] - public string WorkspaceJson { get; set; } = string.Empty; + /// The agent's declared `model:` entry, serialized. A single name or an ordered list of acceptable names. + [JsonPropertyName("agentModelsJson")] + public string AgentModelsJson { get; set; } = string.Empty; + + /// The models available to this session, serialized in the shape the model list carries. + [JsonPropertyName("availableModelsJson")] + public string AvailableModelsJson { get; set; } = string.Empty; } -/// A working-directory context together with the client that produced it. +/// Installed plugin record from global state, with marketplace, version, install time, enabled state, cache path, and source. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionWorkingDirectoryContextWithClient +public sealed class InstalledPlugin { - /// Merge-base commit SHA. - [JsonPropertyName("baseCommit")] - public string? BaseCommit { get; set; } + /// Path where the plugin is cached locally. + [JsonPropertyName("cache_path")] + public string? CachePath { get; set; } - /// Current git branch name. - [JsonPropertyName("branch")] - public string? Branch { get; set; } + /// Whether the plugin is currently enabled. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } - /// Name of the client that created the session. - [JsonPropertyName("clientName")] - public string? ClientName { get; set; } + /// Installation timestamp. + [JsonPropertyName("installed_at")] + public string InstalledAt { get; set; } = string.Empty; - /// Current working directory path. - [JsonPropertyName("cwd")] - public string Cwd { get; set; } = string.Empty; + /// Absolute path of the marketplace directory a live plugin was resolved from. Present only on live, never-persisted records — those synthesized at session start for a directory/local marketplace, whose cache_path points at the real plugin directory on disk rather than a copy under the installed-plugins cache. Its presence is what marks a record as live, and no record carrying it is ever written to the persisted installedPlugins key. + [JsonPropertyName("installed_from")] + public string? InstalledFrom { get; set; } - /// Root directory of the git repository. - [JsonPropertyName("gitRoot")] - public string? GitRoot { get; set; } + /// Marketplace the plugin came from (empty string for direct repo installs). + [JsonPropertyName("marketplace")] + public string Marketplace { get; set; } = string.Empty; - /// Head commit of the current git branch. - [JsonPropertyName("headCommit")] - public string? HeadCommit { get; set; } + /// Plugin name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Hosting platform type of the repository. - [JsonPropertyName("hostType")] - public string? HostType { get; set; } + /// Source for direct repo installs (when marketplace is empty). + [JsonPropertyName("source")] + public JsonElement? Source { get; set; } - /// Repository identifier derived from the git remote URL. - [JsonPropertyName("repository")] - public string? Repository { get; set; } + /// Per-plugin source fingerprint (a SHA-256 hash of the plugin's catalog source spec plus its resolved source subtree — NOT a Git commit SHA) captured at marketplace install/update time. Auto-update compares it against the freshly recomputed fingerprint to detect a content change that does not bump the version. Absent for pre-existing installs and for direct (non-marketplace) installs. + [JsonPropertyName("source_sha")] + public string? SourceSha { get; set; } - /// Raw host string from the git remote URL. - [JsonPropertyName("repositoryHost")] - public string? RepositoryHost { get; set; } + /// Version installed (if available). + [JsonPropertyName("version")] + public string? Version { get; set; } } -/// Identity, state location and starting context for a workspace record. +/// An account the host has signed in to, identified by the server it lives on and the login it uses there. The same person can appear more than once when they use both github.com and an Enterprise server. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsCreateWorkspaceRequest +internal sealed class LoggedInUser { - /// Starting working-directory context. The record keeps `cwd`, `gitRoot`, `repository`, `hostType`, `branch`, and `clientName`. Other fields, including `repositoryHost`, `headCommit`, and `baseCommit`, are ignored. `hostType` must be `github` or `ado`. - [JsonPropertyName("context")] - public SessionWorkingDirectoryContextWithClient? Context { get; set; } - - /// `windows` (any letter case) selects Windows path rules. Any other value selects POSIX path rules. - [JsonPropertyName("convention")] - public string Convention { get; set; } = string.Empty; + /// Source account this account was derived from, when one was recorded. + [JsonPropertyName("derivedFrom")] + public string? DerivedFrom { get; set; } - /// User-supplied display name for the workspace. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// Host the account belongs to, such as `github.com` or an Enterprise server. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; - /// Session ID the workspace record belongs to. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Account kind, when the host recorded one. Consumers must tolerate new strings. + [JsonPropertyName("kind")] + public string? Kind { get; set; } - /// Directory the session's state is written under when no session filesystem provider is configured. Ignored when a provider is configured; the provider's session state path is used instead. - [JsonPropertyName("sessionStatePath")] - public string SessionStatePath { get; set; } = string.Empty; + /// Account login on that host. + [JsonPropertyName("login")] + public string Login { get; set; } = string.Empty; } -/// The workspace record on disk, omitted when the session has none. +/// The host's machine-wide state. Every field is optional because a fresh install has recorded nothing yet, so a reader must treat an absent field as `not yet`, never as a negative answer. Stored credentials are deliberately absent from this shape. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsLoadWorkspaceResult +internal sealed class GlobalStateLoadResult { - /// The workspace record, as JSON. Omitted when the record does not exist. - [JsonPropertyName("workspaceJson")] - public string? WorkspaceJson { get; set; } -} + /// Whether the app tip has been shown. + [JsonPropertyName("appTipShown")] + public bool? AppTipShown { get; set; } -/// Where the session's state lives, as a root directory and the session ID under it. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsLoadWorkspaceRequest -{ - /// Session ID naming the state directory under the sessions home. Rejected when it is absolute or contains a parent component, so it cannot escape the sessions home. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Terminals the user has already been asked to set up, so the host does not ask twice. + [JsonPropertyName("askedSetupTerminals")] + public IList? AskedSetupTerminals { get; set; } - /// Root directory every session's state directory sits under. - [JsonPropertyName("sessionsHome")] - public string SessionsHome { get; set; } = string.Empty; + /// When the Auto-feedback hint was last shown, as an ISO 8601 timestamp. It enforces the once-per-day cap for non-staff users across restarts. + [JsonPropertyName("autoFeedbackLastPromptedAt")] + public string? AutoFeedbackLastPromptedAt { get; set; } + + /// When the host first ran on this machine. + [JsonPropertyName("firstLaunchAt")] + public string? FirstLaunchAt { get; set; } + + /// Plugins installed on this machine. + [JsonPropertyName("installedPlugins")] + public IList? InstalledPlugins { get; set; } + + /// Account used for the most recent sign-in. + [JsonPropertyName("lastLoggedInUser")] + public LoggedInUser? LastLoggedInUser { get; set; } + + /// Every account the host has signed in to on this machine. + [JsonPropertyName("loggedInUsers")] + public IList? LoggedInUsers { get; set; } + + /// Whether the one-off cleanup of stored reasoning summaries has run. + [JsonPropertyName("reasoningSummariesCleanupDone")] + public bool? ReasoningSummariesCleanupDone { get; set; } + + /// Models the user selected recently, most recent first. + [JsonPropertyName("recentModelIds")] + public IList? RecentModelIds { get; set; } + + /// Whether the user declined to trust the sandbox credential proxy CA. + [JsonPropertyName("sandboxCredentialProxyCaDeclined")] + public bool? SandboxCredentialProxyCaDeclined { get; set; } + + /// Whether the sandbox onboarding has been shown. + [JsonPropertyName("sandboxOnboardingShown")] + public bool? SandboxOnboardingShown { get; set; } + + /// Whether the user is a GitHub or Microsoft staff member, which unlocks internal-only behavior. + [JsonPropertyName("staff")] + public bool? Staff { get; set; } + + /// Whether the user was recognized as GitHub staff. + [JsonPropertyName("staffGithub")] + public bool? StaffGitHub { get; set; } + + /// When the staff-only log level migration last ran. + [JsonPropertyName("staffLogLevelMigrationAt")] + public string? StaffLogLevelMigrationAt { get; set; } + + /// Whether the user was recognized as Microsoft staff. + [JsonPropertyName("staffMicrosoft")] + public bool? StaffMicrosoft { get; set; } + + /// When the staff-only model reset last ran. + [JsonPropertyName("staffModelResetAt")] + public string? StaffModelResetAt { get; set; } + + /// When the staff-only update channel migration last ran. + [JsonPropertyName("staffUpdateChannelMigrationAt")] + public string? StaffUpdateChannelMigrationAt { get; set; } + + /// Folders where the user declined the init prompt, so it stays hidden there. + [JsonPropertyName("suppressInitFolders")] + public IList? SuppressInitFolders { get; set; } + + /// Folders the user has marked as trusted. + [JsonPropertyName("trustedFolders")] + public IList? TrustedFolders { get; set; } } -/// The merge completed. The record carries the supplied workspace-schema fields, but a stored `fork_count` stays. +/// Selects the configuration directory whose machine-wide state to read. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsUpdateWorkspaceFieldsResult +internal sealed class GlobalStateLoadForConfigDirRequest { + /// Copilot configuration directory to read the state document from, taking precedence over the server's own `COPILOT_HOME` and default home. Omit it, or pass an empty string, to read the directory the server resolved for itself. + [JsonPropertyName("configDir")] + public string? ConfigDir { get; set; } } -/// Where the session's state lives, plus workspace-schema fields to merge into its workspace record. Stored keys outside the schema are not preserved, and a stored `fork_count` is never replaced. +/// A single top-level key to record in the host's machine-wide state. The write replaces only that key and leaves the rest of the document untouched, so two writers recording different one-off flags do not overwrite each other. The stored credential keys cannot be written through this method. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsUpdateWorkspaceFieldsRequest +internal sealed class GlobalStateWriteKeyRequest { - /// Workspace-schema fields to merge into the record, as a JSON object. Fields the object omits keep their stored values, except stored keys outside the schema are not preserved and a stored `fork_count` is never replaced. - [JsonPropertyName("fieldsJson")] - public string FieldsJson { get; set; } = string.Empty; + /// Copilot configuration directory to write the state document in, taking precedence over the server's own `COPILOT_HOME` and default home. Omit it, or pass an empty string, to write the directory the server resolved for itself. Mirrors `globalState.loadForConfigDir`, so a caller can read and write the same directory. + [JsonPropertyName("configDir")] + public string? ConfigDir { get; set; } - /// Session ID naming the state directory under the sessions home. Rejected when it is absolute or contains a parent component, so it cannot escape the sessions home. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Top-level key to write, named as it appears in the result of `globalState.load`. It must be one of the writable keys that `globalState.writeKey` lists. + [JsonPropertyName("key")] + public string Key { get; set; } = string.Empty; - /// Root directory every session's state directory sits under. - [JsonPropertyName("sessionsHome")] - public string SessionsHome { get; set; } = string.Empty; + /// Value to store for the key. Omit it, or pass null, to remove the key instead. + [JsonPropertyName("value")] + public JsonElement? Value { get; set; } } -/// 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. +/// A literal choice the command input accepts, with a human-facing description. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionsReloadPluginHooksResult +public sealed class SlashCommandInputChoice { + /// Human-readable description shown alongside the choice. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; + + /// The literal choice value (e.g. 'on', 'off', 'show'). + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// Active session ID and an optional flag for deferring repo-level hooks until folder trust. +/// Optional unstructured input hint. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsReloadPluginHooksRequest +public sealed class SlashCommandInput { - /// When true, skip repo-level hooks. Use before folder trust is confirmed; loadDeferredRepoHooks loads them post-trust. - [JsonPropertyName("deferRepoHooks")] - public bool? DeferRepoHooks { get; set; } + /// Optional literal choices the input accepts, each with a human-facing description; clients may render these as selectable options. + [JsonPropertyName("choices")] + public IList? Choices { get; set; } - /// Active session ID to reload hooks for. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Optional completion hint for the input (e.g. 'directory' for filesystem path completion). + [JsonPropertyName("completion")] + public SlashCommandInputCompletion? Completion { get; set; } + + /// Hint to display when command input has not been provided. + [JsonPropertyName("hint")] + public string Hint { get; set; } = string.Empty; + + /// When true, clients should pass the full text after the command name as a single argument rather than splitting on whitespace. + [JsonPropertyName("preserveMultilineInput")] + public bool? PreserveMultilineInput { get; set; } + + /// When true, the command requires non-empty input; clients should render the input hint as required. + [JsonPropertyName("required")] + public bool? Required { get; set; } } -/// Queued repo-level startup prompts and the total hook command count after loading. +/// Slash-command metadata with name, aliases, description, kind, input hint, execution allowance, and schedulability. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionLoadDeferredRepoHooksResult +public sealed class SlashCommandInfo { - /// Total hook command count (user + plugin + repo) loaded for the session by this call. Captured atomically with startupPrompts so callers don't need to read a separate counter. - [JsonPropertyName("hookCount")] - public long HookCount { get; set; } + /// Canonical aliases without leading slashes. + [JsonPropertyName("aliases")] + public IList? Aliases { get; set; } - /// Repo-level startup prompts queued from repo hook configs. Empty on resume, when no repo configs were pending, or when disableAllHooks is set. - [JsonPropertyName("startupPrompts")] - public IList StartupPrompts { get => field ??= []; set; } + /// Whether the command may run while an agent turn is active. + [JsonPropertyName("allowDuringAgentExecution")] + public bool AllowDuringAgentExecution { get; set; } + + /// Human-readable command description. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; + + /// Whether the command is experimental. + [JsonPropertyName("experimental")] + public bool? Experimental { get; set; } + + /// Optional unstructured input hint. + [JsonPropertyName("input")] + public SlashCommandInput? Input { get; set; } + + /// Coarse command category for grouping and behavior: runtime built-in, skill-backed command, or SDK/client-owned command. + [JsonPropertyName("kind")] + public SlashCommandKind Kind { get; set; } + + /// Canonical command name without a leading slash. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Whether the command may be the target of `/every` / `/after` schedules. Resolution happens at every tick, so only set this when the command is safe to re-invoke and produces an agent prompt. + [JsonPropertyName("schedulable")] + public bool? Schedulable { get; set; } } -/// Active session ID whose deferred repo-level hooks should be loaded. +/// Slash commands available in the session, after applying any include/exclude filters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsLoadDeferredRepoHooksRequest +public sealed class CommandList { - /// Active session ID whose deferred repo-level hooks should be loaded. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Commands available in this session. + [JsonPropertyName("commands")] + public IList Commands { get => field ??= []; set; } } -/// 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. +/// Owner, name, and host of a GitHub repository, as resolved from a git remote URL. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionsSetAdditionalPluginsResult +public sealed class GitHubRepositoryIdentity { + /// Host the remote points at, for example `github.com` or a GitHub Enterprise hostname. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; + + /// Repository name, without the owner prefix or the `.git` suffix. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Repository owner login (user or organization). + [JsonPropertyName("owner")] + public string Owner { get; set; } = string.Empty; } -/// Manager-wide additional plugins to register; replaces any previously-configured set. +/// The GitHub repository that owns the requested path, when the selected remote (`origin`, else the first) is on a GitHub host. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsSetAdditionalPluginsRequest +internal sealed class GitHubRepositoryAtPathResult { - /// Manager-wide additional plugins to register. Replaces any previously-configured set. Pass an empty array to clear. - [JsonPropertyName("plugins")] - public IList Plugins { get => field ??= []; set; } + /// Resolved repository identity, or null when the selected remote resolves to no GitHub host. + [JsonPropertyName("repository")] + public GitHubRepositoryIdentity? Repository { get; set; } } -/// Dynamic-context board entry count, when available. +/// Working-tree path whose owning GitHub repository should be resolved. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsGetBoardEntryCountResult +internal sealed class GitHubRepositoryAtPathRequest { - /// Board entry count, when available. - [JsonPropertyName("count")] - public long? Count { get; set; } + /// Absolute path to a directory inside the git working tree to resolve. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; } -/// Session ID whose board entry count should be returned. +/// A freshly registered request id. Registering it before the listing starts is what lets a cancel that races the request still find the owner listing slot. The id serves one listing only. Long-abandoned unused ids can be released by later allocations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsGetBoardEntryCountRequest +internal sealed class GitHubOwnersRequestIdResult { - /// Session ID whose board entry count should be returned. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Request id to pass to `gitHubOwners.list` and, to abandon it, `gitHubOwners.cancel`. + [JsonPropertyName("requestId")] + public long RequestId { get; set; } } -/// State of the runtime-managed remote-control singleton. -/// Polymorphic base type discriminated by state. +/// A GitHub login the authenticated user may act as: their own account, or an organization they belong to. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "state", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(RemoteControlStatusOff), "off")] -[JsonDerivedType(typeof(RemoteControlStatusConnecting), "connecting")] -[JsonDerivedType(typeof(RemoteControlStatusActive), "active")] -[JsonDerivedType(typeof(RemoteControlStatusError), "error")] -public partial class RemoteControlStatus +internal sealed class GitHubOwnerOption { - /// The type discriminator. - [JsonPropertyName("state")] - public virtual string State { get; set; } = string.Empty; -} + /// The owner's GitHub login. + [JsonPropertyName("login")] + public string Login { get; set; } = string.Empty; + /// Which kind of owner this is. The authenticated user's own account is always reported as `user`. + [JsonPropertyName("type")] + public string Type { get; set; } = string.Empty; +} -/// Remote control is not connected. -/// The off variant of . +/// Outcome of an owner listing. Exactly one of `owners` and `message` is present, except that `throwError` reports a failure the caller is expected to raise rather than render. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class RemoteControlStatusOff : RemoteControlStatus +internal sealed class GitHubOwnersListResult { - /// - [JsonIgnore] - public override string State => "off"; + /// Why no owners could be listed, phrased for a user. Present when the listing failed in a way the caller should render rather than raise. + [JsonPropertyName("message")] + public string? Message { get; set; } + + /// The owners, on success: the authenticated user first, then the organizations they belong to. + [JsonPropertyName("owners")] + public IList? Owners { get; set; } + + /// A malformed request or an unreadable credential, which the caller raises instead of rendering. Kept a field rather than a dispatch error so it stays distinct from `message`, which the caller renders. + [JsonPropertyName("throwError")] + public string? ThrowError { get; set; } + + /// A line the caller should log. Present only alongside `message`, and only for failures worth recording. + [JsonPropertyName("warning")] + public string? Warning { get; set; } } -/// Remote control is in the middle of initial setup. -/// The connecting variant of . +/// Credential to list owners under, and the request id that makes the listing cancellable. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class RemoteControlStatusConnecting : RemoteControlStatus +internal sealed class GitHubOwnersListRequest { - /// - [JsonIgnore] - public override string State => "connecting"; + /// The credential the listing runs under, carried opaquely because its shape is the host's own and the runtime only resolves a token and a GitHub host from it. No credential travels: this selects one the runtime already holds. + [JsonPropertyName("authInfo")] + public JsonElement AuthInfo { get; set; } - /// Session id the connection is attaching to. - [JsonPropertyName("attachedSessionId")] - public required string AttachedSessionId { get; set; } + /// Request id from `gitHubOwners.nextRequestId`. An id that was never registered, canceled before use, released after being abandoned, or already used is refused rather than silently running uncancellable. + [JsonPropertyName("requestId")] + public long RequestId { get; set; } } -/// Remote control is connected to a local session. -/// The active variant of . +/// Whether the id named a running owner listing. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class RemoteControlStatusActive : RemoteControlStatus +internal sealed class GitHubOwnersCancelResult { - /// - [JsonIgnore] - public override string State => "active"; - - /// Session id remote control is pointed at. - [JsonPropertyName("attachedSessionId")] - public required string AttachedSessionId { get; set; } - - /// True while a read-only/session-sync export is deferred, awaiting the first `user.message` before its MC session exists. Marked internal: this field is excluded from the public SDK surface and is populated only on the CLI in-process path. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonInclude] - [JsonPropertyName("awaitingFirstMessage")] - internal bool? AwaitingFirstMessage { get; set; } - - /// MC frontend URL for this session, when known. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("frontendUrl")] - public string? FrontendUrl { get; set; } - - /// Whether the MC session may steer this session. - [JsonPropertyName("isSteerable")] - public required bool IsSteerable { get; set; } + /// True when a listing with the id was running and the cancel stopped it. False when the id was never registered, was registered but unused, was released after being abandoned, or its listing had ended. An unused id is released and cannot start a later listing. + [JsonPropertyName("canceled")] + public bool Canceled { get; set; } } -/// The last setup attempt failed. The singleton is otherwise off. -/// The error variant of . +/// The owner listing to abandon. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class RemoteControlStatusError : RemoteControlStatus +internal sealed class GitHubOwnersCancelRequest { - /// - [JsonIgnore] - public override string State => "error"; - - /// Session id the failing setup attempt targeted, when known. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("attachedSessionId")] - public string? AttachedSessionId { get; set; } - - /// Human-readable error message from the last setup attempt. - [JsonPropertyName("error")] - public required string Error { get; set; } + /// Request id the listing was started with. + [JsonPropertyName("requestId")] + public long RequestId { get; set; } } -/// Wrapper for the singleton's current status. +/// The remote the checked-out branch tracks. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class RemoteControlStatusResult +internal sealed class GitCurrentBranchRemoteResult { - /// State of the runtime-managed remote-control singleton. - [JsonPropertyName("status")] - public RemoteControlStatus Status { get => field ??= new(); set; } + /// Name of the tracked remote. Reports `origin` whenever the working tree has no tracking configuration to read, including on a detached HEAD, so this is never null and never empty. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("remote")] + public string Remote { get; set; } = string.Empty; } -/// Reattach to an existing MC session without creating a new one. +/// Working-tree path a git query applies to. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class RemoteControlConfigExistingMcSession +internal sealed class GitCwdRequest { - /// Existing MC session ID to reattach to. - [JsonPropertyName("mcSessionId")] - public string McSessionId { get; set; } = string.Empty; - - /// Existing MC task ID for the reattached session. - [JsonPropertyName("mcTaskId")] - public string McTaskId { get; set; } = string.Empty; + /// Absolute path to a directory inside the git working tree to query. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("cwd")] + public string Cwd { get; set; } = string.Empty; } -/// Configuration for the runtime-managed remote-control singleton. +/// Updated working directory and git context. Emitted as the new payload of `session.context_changed`. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class RemoteControlConfig +public sealed class SessionWorkingDirectoryContext { - /// Reattach to an existing MC session without creating a new one. - [JsonPropertyName("existingMcSession")] - public RemoteControlConfigExistingMcSession? ExistingMcSession { get; set; } + /// Merge-base commit SHA (fork point from the remote default branch). + [JsonPropertyName("baseCommit")] + public string? BaseCommit { get; set; } - /// Whether the user explicitly requested remote (vs. implicit session-sync). Controls warning surfacing for missing-repo cases. - [JsonPropertyName("explicit")] - public bool Explicit { get; set; } + /// Current git branch name. + [JsonPropertyName("branch")] + public string? Branch { get; set; } - /// Whether remote export should be enabled. - [JsonPropertyName("remote")] - public bool Remote { get; set; } + /// Current working directory path. + [JsonPropertyName("cwd")] + public string Cwd { get; set; } = string.Empty; - /// When true, suppresses timeline messages on successful setup. - [JsonPropertyName("silent")] - public bool Silent { get; set; } + /// Root directory of the git repository, resolved via git rev-parse. + [JsonPropertyName("gitRoot")] + public string? GitRoot { get; set; } - /// Whether the MC session may steer the local session (write mode). - [JsonPropertyName("steerable")] - public bool Steerable { get; set; } + /// Head commit of the current git branch. + [JsonPropertyName("headCommit")] + public string? HeadCommit { get; set; } - /// Existing Mission Control task ID to attach the exported session to. - [JsonPropertyName("taskId")] - public string? TaskId { get; set; } + /// Hosting platform type of the repository. + [JsonPropertyName("hostType")] + public SessionWorkingDirectoryContextHostType? HostType { get; set; } + + /// Repository identifier derived from the git remote URL ("owner/name" for GitHub, "org/project/repo" for Azure DevOps). + [JsonPropertyName("repository")] + public string? Repository { get; set; } + + /// Raw host string from the git remote URL (e.g. "github.com", "dev.azure.com"). + [JsonPropertyName("repositoryHost")] + public string? RepositoryHost { get; set; } } -/// Parameters for attaching the remote-control singleton to a session. +/// A GitHub repository one of a working tree's remotes points at. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsStartRemoteControlRequest +internal sealed class GitRemoteRepository { - /// Configuration for the runtime-managed remote-control singleton. - [JsonPropertyName("config")] - public RemoteControlConfig Config { get => field ??= new(); set; } + /// GitHub host serving the repository, which is not `github.com` for a GitHub Enterprise remote. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; - /// Local session id to attach remote control to. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Repository name, without the owner. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Account or organization owning the repository. + [JsonPropertyName("owner")] + public string Owner { get; set; } = string.Empty; + + /// Name of the first remote that produced this distinct repository entry, such as `origin` or `upstream`. + [JsonPropertyName("remoteName")] + public string RemoteName { get; set; } = string.Empty; } -/// Outcome of a transferRemoteControl call. +/// The GitHub repositories a working tree's remotes point at. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class RemoteControlTransferResult +internal sealed class GitReposFromRemotesResult { - /// State of the runtime-managed remote-control singleton. - [JsonPropertyName("status")] - public RemoteControlStatus Status { get => field ??= new(); set; } - - /// Whether the rebinding actually happened. - [JsonPropertyName("transferred")] - public bool Transferred { get; set; } + /// One entry per distinct GitHub repository, in the order git reports the first remote for each repository. Empty when no remote points at a GitHub host, which a caller should read as `not connected to GitHub`. Failing to read the remotes is an error, not an empty list. + [JsonPropertyName("repositories")] + public IList Repositories { get => field ??= []; set; } } -/// Parameters for atomically rebinding the remote-control singleton. +/// Git working tree whose GitHub remotes should be listed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsTransferRemoteControlRequest +internal sealed class GitReposFromRemotesRequest { - /// When provided, the transfer is rejected unless the singleton currently points at this session id (compare-and-swap semantics to avoid clobbering newer state). - [JsonPropertyName("expectedFromSessionId")] - public string? ExpectedFromSessionId { get; set; } - - /// Local session id to point remote control at. - [JsonPropertyName("toSessionId")] - public string ToSessionId { get; set; } = string.Empty; + /// Absolute path to the root of the git working tree. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("gitRoot")] + public string GitRoot { get; set; } = string.Empty; } -/// Patch for the singleton's steering state. +/// Indicates whether the calling client was registered as the session filesystem provider. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsSetRemoteControlSteeringRequest +public sealed class SessionFsSetProviderResult { - /// Target steering state. Today only `true` is actionable on the underlying exporter; `false` is reserved for future use. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } + /// Whether the provider was set successfully. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Outcome of a stopRemoteControl call. +/// Optional capabilities declared by the provider. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class RemoteControlStopResult +public sealed class SessionFsSetProviderCapabilities { - /// State of the runtime-managed remote-control singleton. - [JsonPropertyName("status")] - public RemoteControlStatus Status { get => field ??= new(); set; } + /// Whether the provider supports binary reads and writes through sessionFs.readFileBytes and sessionFs.writeFileBytes. + [JsonPropertyName("binary")] + public bool? Binary { get; set; } - /// Whether the singleton was actually torn down by this call. - [JsonPropertyName("stopped")] - public bool Stopped { get; set; } + /// Whether the provider supports SQLite query/exists operations. + [JsonPropertyName("sqlite")] + public bool? Sqlite { get; set; } } -/// RPC data type for SessionsStopRemoteControl operations. +/// Initial working directory, session-state path layout, and path conventions used to register the calling SDK client as the session filesystem provider. A registered provider is authoritative for path interpretation and filesystem facts used by workspace permission validation. Paths are interpreted lexically; home-relative paths (`~` and `~/...`) and Windows drive-relative paths such as `C:foo` are unsupported. Until provider-side canonicalization is supported, providers must not expose symlinks inside allowed roots that escape those roots. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsStopRemoteControlRequest +internal sealed class SessionFsSetProviderRequest { - /// When provided, the stop is rejected unless the singleton currently points at this session id (compare-and-swap semantics). - [JsonPropertyName("expectedSessionId")] - public string? ExpectedSessionId { get; set; } + /// Optional capabilities declared by the provider. + [JsonPropertyName("capabilities")] + public SessionFsSetProviderCapabilities? Capabilities { get; set; } - /// When true, the singleton is unconditionally torn down regardless of `expectedSessionId`. Use during shutdown or explicit `/remote off`. - [JsonPropertyName("force")] - public bool? Force { get; set; } + /// Path conventions used by this filesystem. + [JsonPropertyName("conventions")] + public SessionFsSetProviderConventions Conventions { get; set; } + + /// Absolute initial working directory for sessions. Registering the provider establishes this path as the root of its virtual namespace; the runtime does not require the provider to materialize or stat it before creating a session. + [JsonPropertyName("initialCwd")] + public string InitialCwd { get; set; } = string.Empty; + + /// Path within each session's SessionFs where the runtime stores files for that session. + [JsonPropertyName("sessionStatePath")] + public string SessionStatePath { get; set; } = string.Empty; } -/// Params to attach or detach an in-process ExtensionController delegate. +/// Indicates whether the calling client was registered as the LLM inference provider. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ConfigureSessionExtensionsParams +public sealed class LlmInferenceSetProviderResult { - /// Session to attach the extension controller delegate to. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether the provider was set successfully. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Outcome of an agentRegistry.spawn call. -/// Polymorphic base type discriminated by kind. +/// Whether the start frame was accepted. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(AgentRegistrySpawnResultSpawned), "spawned")] -[JsonDerivedType(typeof(AgentRegistrySpawnResultSpawnError), "spawn-error")] -[JsonDerivedType(typeof(AgentRegistrySpawnResultRegistryTimeout), "registry-timeout")] -[JsonDerivedType(typeof(AgentRegistrySpawnResultValidationError), "validation-error")] -public partial class AgentRegistrySpawnResult +public sealed class LlmInferenceHttpResponseStartResult { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; + /// True when the response start was matched to a pending request; false when unknown. + [JsonPropertyName("accepted")] + public bool Accepted { get; set; } } - -/// Full registry entry for the spawned child. Lets the controller call `handleLiveTargetSelected(entry)` directly without re-reading the registry (avoids a TOCTOU window). +/// Response head. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AgentRegistryLiveTargetEntry +internal sealed class LlmInferenceHttpResponseStartRequest { - /// Kind of attention required when status === "attention". Meaningful only when status === "attention". - [JsonPropertyName("attentionKind")] - public AgentRegistryLiveTargetEntryAttentionKind? AttentionKind { get; set; } - - /// Git branch of the session (when known). - [JsonPropertyName("branch")] - public string? Branch { get; set; } + /// HTTP response headers, preserving multiple values per name. + [JsonPropertyName("headers")] + public IDictionary> Headers { get => field ??= new Dictionary>(); set; } - /// Copilot CLI version that wrote the entry. - [JsonPropertyName("copilotVersion")] - public string CopilotVersion { get; set; } = string.Empty; + /// Matches the requestId from the originating httpRequestStart frame. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; - /// Working directory of the session (when known). - [JsonPropertyName("cwd")] - public string? Cwd { get; set; } + /// HTTP status code. + [JsonPropertyName("status")] + public long Status { get; set; } - /// Bind host for the entry's JSON-RPC server. - [JsonPropertyName("host")] - public string Host { get; set; } = string.Empty; + /// Optional HTTP status reason phrase. + [JsonPropertyName("statusText")] + public string? StatusText { get; set; } +} - /// Process kind tag for the registry entry. - [JsonPropertyName("kind")] - public AgentRegistryLiveTargetEntryKind Kind { get; set; } +/// Whether the chunk was accepted. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class LlmInferenceHttpResponseChunkResult +{ + /// True when the chunk was matched to a pending request; false when unknown. + [JsonPropertyName("accepted")] + public bool Accepted { get; set; } +} - /// Wall-clock milliseconds since the watcher last observed this entry (heartbeat freshness). - [JsonPropertyName("lastSeenMs")] - public long LastSeenMs { get; set; } +/// Set to terminate the response with a transport-level failure. Implies end-of-stream; any further chunks for this requestId are ignored. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class LlmInferenceHttpResponseChunkError +{ + /// Optional machine-readable error code. + [JsonPropertyName("code")] + public string? Code { get; set; } - /// How the most recent turn ended (clean vs aborted). Lets the renderer distinguish done from done_cancelled. - [JsonPropertyName("lastTerminalEvent")] - public AgentRegistryLiveTargetEntryLastTerminalEvent? LastTerminalEvent { get; set; } + /// Human-readable failure description. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; +} - /// Model identifier currently selected for the session. - [JsonPropertyName("model")] - public string? Model { get; set; } +/// A response body chunk or terminal error. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class LlmInferenceHttpResponseChunkRequest +{ + /// When true, `data` is base64-encoded bytes. When absent or false, `data` is UTF-8 text. + [JsonPropertyName("binary")] + public bool? Binary { get; set; } - /// Operating-system pid of the process owning this entry. - [JsonPropertyName("pid")] - public long Pid { get; set; } + /// Body byte range. UTF-8 text when `binary` is absent or false; base64-encoded bytes when `binary` is true. May be empty (e.g. when the response body is empty: send a single chunk with empty data and end=true). + [JsonPropertyName("data")] + public string Data { get; set; } = string.Empty; - /// TCP port the entry's JSON-RPC server is listening on. - [JsonPropertyName("port")] - public long Port { get; set; } + /// When true, this is the final body chunk for the response. The runtime treats the response body as complete after receiving an end-marked chunk. + [JsonPropertyName("end")] + public bool? End { get; set; } - /// Registry entry schema version (1 = ui-server, 2 = managed-server). - [JsonPropertyName("schemaVersion")] - public long SchemaVersion { get; set; } + /// Set to terminate the response with a transport-level failure. Implies end-of-stream; any further chunks for this requestId are ignored. + [JsonPropertyName("error")] + public LlmInferenceHttpResponseChunkError? Error { get; set; } - /// Session ID of the foreground session for this entry. - [JsonPropertyName("sessionId")] - public string? SessionId { get; set; } + /// Matches the requestId from the originating httpRequestStart frame. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; +} - /// Friendly session name (when set). - [JsonPropertyName("sessionName")] - public string? SessionName { get; set; } +/// Pre-resolved working-directory context for session startup. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionContext +{ + /// Active git branch. + [JsonPropertyName("branch")] + public string? Branch { get; set; } - /// ISO 8601 timestamp captured at registration. - [JsonPropertyName("startedAt")] - public string StartedAt { get; set; } = string.Empty; + /// Most recent working directory for this session. + [JsonPropertyName("cwd")] + public string Cwd { get; set; } = string.Empty; - /// Coarse lifecycle status of the foreground session. - [JsonPropertyName("status")] - public AgentRegistryLiveTargetEntryStatus? Status { get; set; } + /// Git repository root, if the cwd was inside a git repo. + [JsonPropertyName("gitRoot")] + public string? GitRoot { get; set; } - /// Monotonic per-publisher revision counter incremented on every status update. Lets watchers detect transient flips. - [JsonPropertyName("statusRevision")] - public long? StatusRevision { get; set; } + /// Repository host type. + [JsonPropertyName("hostType")] + public SessionContextHostType? HostType { get; set; } - /// Connection token (null when the target is unauthenticated). - [JsonInclude] - [JsonPropertyName("token")] - internal string? Token { get; set; } + /// Repository slug in `owner/name` form, when known. + [JsonPropertyName("repository")] + public string? Repository { get; set; } } -/// Canonical process-log discovery outcome; populated from spawnLiveTarget. +/// GitHub repository the remote session belongs to. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AgentRegistryLogCapture +public sealed class RemoteSessionMetadataRepository { - /// Whether a canonical process log was discovered for this managed spawn. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } - - /// Why no canonical process log could be opened for this managed spawn (set only when enabled is false). - [JsonPropertyName("openError")] - public string? OpenError { get; set; } + /// Branch associated with the remote session. + [JsonPropertyName("branch")] + public string Branch { get; set; } = string.Empty; - /// Categorized reason no canonical process log could be opened (set only when enabled is false). - [JsonPropertyName("openErrorReason")] - public AgentRegistryLogCaptureOpenErrorReason? OpenErrorReason { get; set; } + /// Repository name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Absolute path to the managed spawn's process-<timestamp>-<pid>.log file (only set when enabled). - [JsonPropertyName("path")] - public string? Path { get; set; } + /// Repository owner. + [JsonPropertyName("owner")] + public string Owner { get; set; } = string.Empty; } -/// Managed-server child was spawned and registered successfully. -/// The spawned variant of . +/// Remote session metadata for the session to hand off (typically obtained from `sessions.list` with `source: "remote"`). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AgentRegistrySpawnResultSpawned : AgentRegistrySpawnResult +public sealed class RemoteSessionMetadataValue { - /// - [JsonIgnore] - public override string Kind => "spawned"; + /// Most recent working directory context. + [JsonPropertyName("context")] + public SessionContext? Context { get; set; } - /// Full registry entry for the spawned child. Lets the controller call `handleLiveTargetSelected(entry)` directly without re-reading the registry (avoids a TOCTOU window). - [JsonPropertyName("entry")] - public required AgentRegistryLiveTargetEntry Entry { get; set; } + /// Host-supplied human description of what the session is doing right now ("running tests", "waiting for approval"). Optional in the protocol and absent on hosts that do not publish it, so never rely on it -- it enriches `hostStatus`, it does not replace it. + [JsonPropertyName("hostActivity")] + public string? HostActivity { get; set; } - /// If the delegate attempted to send the initial prompt and failed, the categorized error message. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("initialPromptError")] - public string? InitialPromptError { get; set; } + /// Live status as the owning host reports it in its session listing, so a row for a session running elsewhere can show that it is running. Absent for hosts that publish no such status (the cloud task managers), which read as idle. + [JsonPropertyName("hostStatus")] + public RemoteSessionHostStatus? HostStatus { get; set; } - /// Whether the delegate already sent the initial prompt. Always omitted in the current wiring: the controller sends the prompt post-attach via the standard LocalRpcSession.send path. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("initialPromptSent")] - public bool? InitialPromptSent { get; set; } + /// Always true for remote sessions. + [JsonPropertyName("isRemote")] + public bool IsRemote { get; set; } - /// Per-spawn log-capture outcome; populated from spawnLiveTarget. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("logCapture")] - public AgentRegistryLogCapture? LogCapture { get; set; } -} + /// Last-modified time as an ISO 8601 timestamp. + [JsonPropertyName("modifiedTime")] + public string ModifiedTime { get; set; } = string.Empty; -/// `child_process.spawn` itself failed before the child entered the registry. -/// The spawn-error variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AgentRegistrySpawnResultSpawnError : AgentRegistrySpawnResult -{ - /// - [JsonIgnore] - public override string Kind => "spawn-error"; + /// Optional human-friendly name set via /rename. + [JsonPropertyName("name")] + public string? Name { get; set; } - /// Underlying errno code (e.g. ENOENT, EACCES) when available. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("code")] - public string? Code { get; set; } + /// Pull request number associated with the session. + [JsonPropertyName("pullRequestNumber")] + public long? PullRequestNumber { get; set; } - /// Human-readable error message. - [JsonPropertyName("message")] - public required string Message { get; set; } -} + /// Backing remote session IDs (most recent first). + [JsonPropertyName("remoteSessionIds")] + public IList RemoteSessionIds { get => field ??= []; set; } -/// Spawn succeeded but the child did not publish a matching managed-server entry within the timeout. -/// The registry-timeout variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AgentRegistrySpawnResultRegistryTimeout : AgentRegistrySpawnResult -{ - /// - [JsonIgnore] - public override string Kind => "registry-timeout"; + /// GitHub repository the remote session belongs to. + [JsonPropertyName("repository")] + public RemoteSessionMetadataRepository Repository { get => field ??= new(); set; } - /// Process ID of the orphaned child (so the caller can offer 'kill the pid' guidance). - [JsonPropertyName("childPid")] - public required long ChildPid { get; set; } + /// Original remote resource identifier (task ID or PR node ID). + [JsonPropertyName("resourceId")] + public string? ResourceId { get; set; } - /// Per-spawn log-capture outcome; populated from spawnLiveTarget. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("logCapture")] - public AgentRegistryLogCapture? LogCapture { get; set; } -} + /// Stable session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; -/// Synchronous pre-validation rejected the spawn request. -/// The validation-error variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AgentRegistrySpawnResultValidationError : AgentRegistrySpawnResult -{ - /// - [JsonIgnore] - public override string Kind => "validation-error"; + /// Deadline (ISO 8601) at which a CLI remote session becomes stale without further heartbeats. + [JsonPropertyName("staleAt")] + public string? StaleAt { get; set; } - /// Which parameter field was invalid. Omitted when the rejection is not field-specific. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("field")] - public AgentRegistrySpawnValidationErrorField? Field { get; set; } + /// Session creation time as an ISO 8601 timestamp. + [JsonPropertyName("startTime")] + public string StartTime { get; set; } = string.Empty; - /// Human-readable explanation; safe to surface in the UI banner. Never logged to unrestricted telemetry. - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Server-side task state returned by GitHub. + [JsonPropertyName("state")] + public string? State { get; set; } - /// Categorized reason for the rejection. Low-cardinality enum so telemetry can aggregate by reason without leaking raw paths or agent/model names. - [JsonPropertyName("reason")] - public required AgentRegistrySpawnValidationErrorReason Reason { get; set; } + /// Short summary of the session, when one has been derived. + [JsonPropertyName("summary")] + public string? Summary { get; set; } + + /// Whether the remote task originated from CCA or CLI `--remote`. + [JsonPropertyName("taskType")] + public RemoteSessionMetadataTaskType? TaskType { get; set; } } -/// Inputs to spawn a managed-server child via the controller's spawn delegate. +/// `sessions.open` handoff progress update with step, status, and optional message. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AgentRegistrySpawnRequest +public sealed class SessionsOpenProgress { - /// Custom or built-in agent name (e.g. 'explore'). When omitted, the child uses its own default. - [JsonPropertyName("agentName")] - public string? AgentName { get; set; } - - /// Working directory for the spawned child (must be an existing directory). - [JsonPropertyName("cwd")] - public string Cwd { get; set; } = string.Empty; - - /// Optional first user message. Forwarded to the caller (the CLI's spawn wrapper sends it post-attach via the standard LocalRpcSession.send path). - [JsonPropertyName("initialPrompt")] - public string? InitialPrompt { get; set; } - - /// Model identifier to apply to the new session. - [JsonPropertyName("model")] - public string? Model { get; set; } + /// Optional step message. + [JsonPropertyName("message")] + public string? Message { get; set; } - /// Friendly session name. Must satisfy validateSessionName: non-empty, no leading/trailing whitespace, <=100 chars, no control chars, no double quotes. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// Step status. + [JsonPropertyName("status")] + public SessionsOpenProgressStatus Status { get; set; } - /// Permission posture for the new session. 'yolo' requires the controller-local session to currently be in allow-all mode. - [JsonPropertyName("permissionMode")] - public AgentRegistrySpawnPermissionMode? PermissionMode { get; set; } + /// Handoff step. + [JsonPropertyName("step")] + public SessionsOpenProgressStep Step { get; set; } } -/// Feature availability. +/// Result of opening a session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorDiscoveryCapabilities +public sealed class SessionOpenResult { - /// API version. - [JsonPropertyName("apiVersion")] - public long ApiVersion { get; set; } + /// Remote session metadata, present when status is `connected`. + [JsonPropertyName("metadata")] + public RemoteSessionMetadataValue? Metadata { get; set; } - /// Availability. - [JsonPropertyName("availability")] - public ConnectorDiscoveryAvailability Availability { get; set; } + /// Handoff progress steps, present when status is `handed_off`. + [JsonPropertyName("progress")] + public IList? Progress { get; set; } - /// Whether results are cached. - [JsonPropertyName("conditionalCache")] - public bool ConditionalCache { get; set; } + /// Remote session ID, present when status is `connected`. + [JsonPropertyName("remoteSessionId")] + public string? RemoteSessionId { get; set; } - /// Whether accounts are selected by opaque ID. - [JsonPropertyName("opaqueAccountSelection")] - public bool OpaqueAccountSelection { get; set; } + /// Opened session ID. Omitted when status is `not_found`. + [JsonPropertyName("sessionId")] + public string? SessionId { get; set; } + + /// Startup prompts queued by user-level hook configs at session creation. Only populated when status is `created`; resumed sessions return an empty array. + [JsonPropertyName("startupPrompts")] + public IList? StartupPrompts { get; set; } + + /// Outcome of the open request. + [JsonPropertyName("status")] + public SessionsOpenStatus Status { get; set; } } -/// Account metadata. +/// Identifier and optional friendly name assigned to the newly forked session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorDiscoveryAuthInfo +public sealed class SessionsForkResult { - /// Host. - [JsonPropertyName("host")] - public string Host { get; set; } = string.Empty; - - /// Login. - [JsonPropertyName("login")] - public string Login { get; set; } = string.Empty; + /// Friendly name assigned to the forked session, if any. + [JsonPropertyName("name")] + public string? Name { get; set; } - /// Authentication type. - [JsonPropertyName("type")] - public AuthInfoType Type { get; set; } + /// The new forked session's ID. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Eligible account. +/// Source session identifier to fork from, optional event-ID boundary, and optional friendly name for the new session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorDiscoveryAccount +internal sealed class SessionsForkRequest { - /// Opaque account ID. - [JsonPropertyName("accountId")] - public string AccountId { get; set; } = string.Empty; + /// Optional friendly name to assign to the forked session. + [JsonPropertyName("name")] + public string? Name { get; set; } - /// Account metadata. - [JsonPropertyName("authInfo")] - public ConnectorDiscoveryAuthInfo AuthInfo { get => field ??= new(); set; } + /// Source session ID to fork from. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; + + /// Optional event ID boundary. When provided, the fork includes only events before this ID (exclusive). When omitted, all events are included. + [JsonPropertyName("toEventId")] + public string? ToEventId { get; set; } } -/// Eligible accounts. +/// Repository associated with the connected remote session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorDiscoveryAccountList +public sealed class ConnectedRemoteSessionMetadataRepository { - /// Eligible accounts. - [JsonPropertyName("accounts")] - public IList Accounts { get => field ??= []; set; } + /// Branch associated with the remote session. + [JsonPropertyName("branch")] + public string Branch { get; set; } = string.Empty; - /// Availability. - [JsonPropertyName("availability")] - public ConnectorDiscoveryAvailability Availability { get; set; } + /// Repository name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Repository owner or organization login. + [JsonPropertyName("owner")] + public string Owner { get; set; } = string.Empty; } -/// Entry. +/// Metadata for a connected remote session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorDiscoveryCatalogEntry +public sealed class ConnectedRemoteSessionMetadata { - /// Description. - [JsonPropertyName("description")] - public string? Description { get; set; } - - /// Display name. - [JsonPropertyName("displayName")] - public string DisplayName { get; set; } = string.Empty; + /// Neutral SDK discriminator for the connected remote session kind. + [JsonPropertyName("kind")] + public ConnectedRemoteSessionMetadataKind Kind { get; set; } - /// Logo. - [JsonPropertyName("logo")] - public string? Logo { get; set; } + /// Last session update time as an ISO 8601 string. + [JsonPropertyName("modifiedTime")] + public DateTimeOffset ModifiedTime { get; set; } - /// Name. + /// Optional friendly session name. [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + public string? Name { get; set; } - /// Release tag. - [JsonPropertyName("releaseTag")] - public string? ReleaseTag { get; set; } + /// Pull request number associated with the session. + [JsonPropertyName("pullRequestNumber")] + public long? PullRequestNumber { get; set; } - /// Status. - [JsonPropertyName("status")] - public ConnectorCatalogStatus Status { get; set; } + /// Repository associated with the connected remote session. + [JsonPropertyName("repository")] + public ConnectedRemoteSessionMetadataRepository Repository { get => field ??= new(); set; } - /// Tier. - [JsonPropertyName("tier")] - public string? Tier { get; set; } -} + /// Original remote resource identifier. + [JsonPropertyName("resourceId")] + public string? ResourceId { get; set; } -/// Entries for the selected account. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorDiscoveryCatalogResult -{ - /// Opaque account ID. - [JsonPropertyName("accountId")] - public string AccountId { get; set; } = string.Empty; + /// SDK session ID for the connected remote session. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Entries. - [JsonPropertyName("connectors")] - public IList Connectors { get => field ??= []; set; } + /// Remote session staleness deadline as an ISO 8601 string. + [JsonPropertyName("staleAt")] + public DateTimeOffset? StaleAt { get; set; } - /// Refresh time in Unix epoch milliseconds. - [JsonPropertyName("refreshedAtMs")] - public long RefreshedAtMs { get; set; } + /// Session start time as an ISO 8601 string. + [JsonPropertyName("startTime")] + public DateTimeOffset StartTime { get; set; } - /// Revision. - [JsonPropertyName("revision")] - public long Revision { get; set; } -} + /// Remote session state returned by the backing service. + [JsonPropertyName("state")] + public string? State { get; set; } -/// Selected account. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ConnectorDiscoveryAccountRequest -{ - /// Opaque account ID. - [RegularExpression("^\\S+$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [MaxLength(2048)] - [JsonPropertyName("accountId")] - public string AccountId { get; set; } = string.Empty; + /// Optional session summary. + [JsonPropertyName("summary")] + public string? Summary { get; set; } } -/// Identifies the target session. +/// Remote session connection result. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSuspendRequest +public sealed class RemoteSessionConnectionResult { - /// Target session identifier. + /// Metadata for a connected remote session. + [JsonPropertyName("metadata")] + public ConnectedRemoteSessionMetadata Metadata { get => field ??= new(); set; } + + /// SDK session ID for the connected remote session. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of sending a user message. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SendResult -{ - /// Unique identifier assigned to the message. - [JsonPropertyName("messageId")] - public string MessageId { get; set; } = string.Empty; -} - -/// Provider-native structured output format. JSON Schema is forwarded without rewriting or validating the schema or the generated output. -/// Polymorphic base type discriminated by type. +/// Remote session connection parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "type", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(ResponseFormatJsonSchema), "json_schema")] -public partial class ResponseFormat +internal sealed class ConnectRemoteSessionParams { - /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { get; set; } = string.Empty; + /// Session ID to connect to. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } - -/// A JSON Schema output contract. OpenAI receives the name, description, schema and strict setting; Anthropic receives the schema in output_config.format and always uses its native strict enforcement. +/// Local or remote session metadata entry. Narrow on `isRemote` to access source-specific fields. +/// Data type discriminated by isRemote. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class JsonSchemaResponseFormat +public partial class SessionListEntry { - /// Optional description passed to OpenAI providers. - [JsonPropertyName("description")] - public string? Description { get; set; } - - /// Name of the output schema, subject to the provider's naming restrictions. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - /// JSON Schema passed unchanged to the inference provider. Schemas larger than 32 MiB when JSON-encoded are rejected before admission, using the runtime's existing request-size ceiling. This is not a guarantee that the entire model request fits. Supported keywords and schema restrictions are determined by the provider. - [JsonPropertyName("schema")] - public JsonElement Schema { get; set; } - - /// Optional strict enforcement setting for OpenAI providers. Omitted uses the provider default. Anthropic always enforces its supported schema subset. - [JsonPropertyName("strict")] - public bool? Strict { get; set; } -} + /// The boolean discriminator. + [JsonPropertyName("isRemote")] + public bool IsRemote { get; set; } -/// The json_schema variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ResponseFormatJsonSchema : ResponseFormat -{ - /// - [JsonIgnore] - public override string Type => "json_schema"; + /// Runtime client name that created/last resumed this session. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("clientName")] + public string? ClientName { get; set; } - /// JSON Schema and provider options for the turn's output. - [JsonPropertyName("jsonSchema")] - public required JsonSchemaResponseFormat JsonSchema { get; set; } -} + /// Pre-resolved working-directory context for session startup. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("context")] + public SessionContext? Context { get; set; } -/// Parameters for sending a user message to the session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SendRequest -{ - /// The UI mode the agent was in when this message was sent. Defaults to the session's current mode. - [JsonPropertyName("agentMode")] - public SendAgentMode? AgentMode { get; set; } + /// Host-supplied human description of what the session is doing right now ("running tests", "waiting for approval"). Optional in the protocol and absent on hosts that do not publish it, so never rely on it -- it enriches `hostStatus`, it does not replace it. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("hostActivity")] + public string? HostActivity { get; set; } - /// Optional attachments (files, directories, selections, blobs, GitHub references) to include with the message. - [JsonPropertyName("attachments")] - public IList? Attachments { get; set; } + /// Live status as the owning host reports it in its session listing, so a row for a session running elsewhere can show that it is running. Absent for hosts that publish no such status (the cloud task managers), which read as idle. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("hostStatus")] + public RemoteSessionHostStatus? HostStatus { get; set; } - /// If false, this message will not trigger a Premium Request Unit charge. User messages default to billable. - [JsonPropertyName("billable")] - public bool? Billable { get; set; } + /// True for detached maintenance sessions that should be hidden from normal resume lists. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("isDetached")] + public bool? IsDetached { get; set; } - /// If provided, this is shown in the timeline instead of `prompt`. - [JsonPropertyName("displayPrompt")] - public string? DisplayPrompt { get; set; } + /// GitHub task ID, when this local session is bound to one. Only present for local sessions exported to remote control. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("mcTaskId")] + public string? McTaskId { get; set; } - /// How to deliver the message. `enqueue` (default) appends to the message queue. `immediate` interjects during an in-progress turn. - [JsonPropertyName("mode")] - public SendMode? Mode { get; set; } + /// Last-modified time of the session's persisted state, as ISO 8601. + [JsonPropertyName("modifiedTime")] + public required string ModifiedTime { get; set; } - /// If true, adds the message to the front of the queue instead of the end. - [JsonPropertyName("prepend")] - public bool? Prepend { get; set; } + /// Optional human-friendly name set via /rename. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("name")] + public string? Name { get; set; } - /// The user message text. - [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; + /// Pull request number associated with the session. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("pullRequestNumber")] + public long? PullRequestNumber { get; set; } - /// Custom HTTP headers to include in outbound model requests for this turn. Merged with session-level provider headers; per-turn headers augment and overwrite session-level headers with the same key. - [JsonPropertyName("requestHeaders")] - public IDictionary? RequestHeaders { get; set; } + /// Backing remote session IDs (most recent first). + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("remoteSessionIds")] + public IList? RemoteSessionIds { get; set; } - /// If set, the request will fail if the named tool is not available when this message is among the user messages at the start of the current exchange. - [JsonPropertyName("requiredTool")] - public string? RequiredTool { get; set; } + /// GitHub repository the remote session belongs to. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("repository")] + public RemoteSessionMetadataRepository? Repository { get; set; } - /// Provider-native output format for this turn, including all tool-call iterations. Not inherited by later turns or subagents. Ordinary steering inherits the active format; specifying responseFormat with mode: immediate is an error, even while idle. Returned assistant content remains text; the runtime does not parse or validate it. Unsupported models or schemas produce provider errors. - [JsonPropertyName("responseFormat")] - public ResponseFormat? ResponseFormat { get; set; } + /// Original remote resource identifier (task ID or PR node ID). + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("resourceId")] + public string? ResourceId { get; set; } - /// Target session identifier. + /// Stable session identifier. [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + public required string SessionId { get; set; } - /// Optional provenance tag copied to the resulting user.message event. Must be `user`, `system`, `command-<command-id>` for command-originated messages, `schedule-<numeric-id>` for scheduled prompts, or `agent-<agent-id>` for prompts sent by another agent. - [RegularExpression("^(user|system|command-.*|schedule-\\d+|agent-.+)$")] - [JsonInclude] - [JsonPropertyName("source")] - internal string? Source { get; set; } + /// Deadline (ISO 8601) at which a CLI remote session becomes stale without further heartbeats. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("staleAt")] + public string? StaleAt { get; set; } - /// W3C Trace Context traceparent header for distributed tracing of this agent turn. - [JsonPropertyName("traceparent")] - public string? Traceparent { get; set; } + /// Session creation time as an ISO 8601 timestamp. + [JsonPropertyName("startTime")] + public required string StartTime { get; set; } - /// W3C Trace Context tracestate header for distributed tracing. - [JsonPropertyName("tracestate")] - public string? Tracestate { get; set; } + /// Server-side task state returned by GitHub. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("state")] + public string? State { get; set; } - /// If true, await completion of the agentic loop for this message before returning. Defaults to false (fire-and-forget). When true, the result still contains the same `messageId`; the caller can rely on the agent having processed the message before the call resolves. Transport-dependent tail semantics: on a LOCAL (in-process) session the wait additionally blocks until the completed turn's event tail has been dispatched to this session's in-process subscribers, so a subsequent read of subscriber state already reflects the turn; on a REMOTE session the wait resolves once the loop completes and mirrored delivery follows over the wire. Callers that need the stronger local guarantee on remote sessions should await the event stream explicitly. - [JsonPropertyName("wait")] - public bool? Wait { get; set; } + /// Short summary of the session, when one has been derived. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("summary")] + public string? Summary { get; set; } + + /// Whether the remote task originated from CCA or CLI `--remote`. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("taskType")] + public RemoteSessionMetadataTaskType? TaskType { get; set; } } -/// Result of sending zero or more user messages. +/// Sessions matching the filter, ordered most-recently-modified first. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SendMessagesResult +public sealed class SessionList { - /// Unique identifiers assigned to the messages, one per provided message in order. For a batch that starts a run, assistant messages use the final ID as originatingMessageId throughout that run, including tool iterations and stop-hook corrections. Immediate steering does not replace the active run's origin. Empty when no messages were provided; that run has no originatingMessageId. - [JsonPropertyName("messageIds")] - public IList MessageIds { get => field ??= []; set; } + /// Sessions ordered most-recently-modified first. Discriminated by `isRemote`. + [JsonPropertyName("sessions")] + public IList Sessions { get => field ??= []; set; } } -/// A single user message to append to the session as part of a `session.sendMessages` turn. +/// Optional filter applied to the returned sessions. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SendMessageItem +public sealed class SessionListFilter { - /// Optional attachments (files, directories, selections, blobs, GitHub references) to include with this message. - [JsonPropertyName("attachments")] - public IList? Attachments { get; set; } + /// Match sessions whose context.branch equals this value. + [JsonPropertyName("branch")] + public string? Branch { get; set; } - /// If false, this message will not trigger a Premium Request Unit charge. User messages default to billable. - [JsonInclude] - [JsonPropertyName("billable")] - internal bool? Billable { get; set; } + /// Match sessions whose context.cwd equals this value. + [JsonPropertyName("cwd")] + public string? Cwd { get; set; } - /// If provided, this is shown in the timeline instead of `prompt`. - [JsonPropertyName("displayPrompt")] - public string? DisplayPrompt { get; set; } + /// Match sessions whose context.gitRoot equals this value. + [JsonPropertyName("gitRoot")] + public string? GitRoot { get; set; } - /// The user message text. - [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; + /// Match sessions whose context.repository equals this value. + [JsonPropertyName("repository")] + public string? Repository { get; set; } +} - /// If set, the request will fail if the named tool is not available when this message is among the user messages at the start of the current exchange. - [JsonPropertyName("requiredTool")] - public string? RequiredTool { get; set; } +/// Optional source filter, metadata-load limit, and context filter applied to the returned sessions. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionsListRequest +{ + /// Optional filter applied to the returned sessions. + [JsonPropertyName("filter")] + public SessionListFilter? Filter { get; set; } - /// Optional provenance tag copied to the resulting user.message event. Must be `user`, `system`, `command-<command-id>` for command-originated messages, `schedule-<numeric-id>` for scheduled prompts, or `agent-<agent-id>` for prompts sent by another agent. - [RegularExpression("^(user|system|command-.*|schedule-\\d+|agent-.+)$")] - [JsonInclude] + /// When true, include detached maintenance sessions. Defaults to false for user-facing session lists. + [JsonPropertyName("includeDetached")] + public bool? IncludeDetached { get; set; } + + /// When provided, only the first N local sessions (sorted by modification time, newest first) load full metadata; remaining sessions return basic info only. Use 0 to return only basic info for every local session. Has no effect on remote entries (which always carry their full shape). + [JsonPropertyName("metadataLimit")] + public long? MetadataLimit { get; set; } + + /// Which session sources to include. Defaults to `local` for backward compatibility. [JsonPropertyName("source")] - internal string? Source { get; set; } + public SessionSource? Source { get; set; } + + /// Only meaningful when `source` includes remote. When true, propagates errors from the remote service instead of silently returning an empty remote list. Defaults to false. + [JsonPropertyName("throwOnError")] + public bool? ThrowOnError { get; set; } } -/// Parameters for sending zero or more user messages to the session in a single turn. Remote-backed (Mission Control) sessions do not support this method and will return an error. +/// Persisted local session metadata, including identifiers, timestamps, summary/name, client, context, detached state, and task ID. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SendMessagesRequest +public sealed class LocalSessionMetadataValue { - /// The UI mode the agent was in when these messages were sent. Defaults to the session's current mode. - [JsonPropertyName("agentMode")] - public SendAgentMode? AgentMode { get; set; } + /// Runtime client name that created/last resumed this session. + [JsonPropertyName("clientName")] + public string? ClientName { get; set; } - /// The user messages to append to the conversation, in order, before running one agent loop. When the batch starts a run, its final message is the primary initiating message; earlier messages provide context, not separate runs or replies. May be empty, in which case a single turn runs over the existing history with no new user message or originatingMessageId. - [JsonPropertyName("messages")] - public IList Messages { get => field ??= []; set; } + /// Pre-resolved working-directory context for session startup. + [JsonPropertyName("context")] + public SessionContext? Context { get; set; } - /// How to deliver the messages. `enqueue` (default) appends to the message queue. `immediate` interjects during an in-progress turn. - [JsonPropertyName("mode")] - public SendMode? Mode { get; set; } + /// True for detached maintenance sessions that should be hidden from normal resume lists. + [JsonPropertyName("isDetached")] + public bool? IsDetached { get; set; } - /// If true, adds the messages to the front of the queue instead of the end. - [JsonPropertyName("prepend")] - public bool? Prepend { get; set; } + /// Always false for local sessions. + [JsonPropertyName("isRemote")] + public bool IsRemote { get; set; } - /// Custom HTTP headers to include in outbound model requests for this turn. Merged with session-level provider headers; per-turn headers augment and overwrite session-level headers with the same key. - [JsonPropertyName("requestHeaders")] - public IDictionary? RequestHeaders { get; set; } + /// GitHub task ID, when this local session is bound to one. Only present for local sessions exported to remote control. + [JsonPropertyName("mcTaskId")] + public string? McTaskId { get; set; } - /// Provider-native output format for the whole turn, including an empty message batch and all tool-call iterations. Not inherited by later turns or subagents. Ordinary steering inherits the active format; specifying responseFormat with mode: immediate is an error, even while idle. Returned assistant content remains text; the runtime does not parse or validate it. Unsupported models or schemas produce provider errors. - [JsonPropertyName("responseFormat")] - public ResponseFormat? ResponseFormat { get; set; } + /// Last-modified time of the session's persisted state, as ISO 8601. + [JsonPropertyName("modifiedTime")] + public string ModifiedTime { get; set; } = string.Empty; - /// Target session identifier. + /// Optional human-friendly name set via /rename. + [JsonPropertyName("name")] + public string? Name { get; set; } + + /// Stable session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - /// W3C Trace Context traceparent header for distributed tracing of this agent turn. - [JsonPropertyName("traceparent")] - public string? Traceparent { get; set; } - - /// W3C Trace Context tracestate header for distributed tracing. - [JsonPropertyName("tracestate")] - public string? Tracestate { get; set; } + /// Session creation time as an ISO 8601 timestamp. + [JsonPropertyName("startTime")] + public string StartTime { get; set; } = string.Empty; - /// If true, await completion of the agentic loop for this turn before returning. Defaults to false (fire-and-forget). When true, the result still contains the same `messageIds`; the caller can rely on the agent having processed the messages before the call resolves. Transport-dependent tail semantics: on a LOCAL (in-process) session the wait additionally blocks until the completed turn's event tail has been dispatched to this session's in-process subscribers, so a subsequent read of subscriber state already reflects the turn; on a REMOTE session the wait resolves once the loop completes and mirrored delivery follows over the wire. Callers that need the stronger local guarantee on remote sessions should await the event stream explicitly. - [JsonPropertyName("wait")] - public bool? Wait { get; set; } + /// Short summary of the session, when one has been derived. + [JsonPropertyName("summary")] + public string? Summary { get; set; } } -/// Internal request for sending a system notification. +/// Persisted local session metadata when the session exists. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SendSystemNotificationRequest +internal sealed class SessionsGetMetadataResult { - /// Optional structured notification kind. - [JsonPropertyName("kind")] - public JsonElement? Kind { get; set; } - - /// Notification text to deliver to the model. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; - - /// Internal delivery options, including passive policy. - [JsonPropertyName("options")] - public JsonElement? Options { get; set; } + /// Local session metadata, omitted when the session does not exist. + [JsonPropertyName("session")] + public LocalSessionMetadataValue? Session { get; set; } +} - /// Target session identifier. +/// Session ID whose persisted metadata should be read. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionsGetMetadataRequest +{ + /// Session ID to inspect. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of aborting the current turn. +/// Client metadata outcome for one requested local session. +/// Polymorphic base type discriminated by status. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AbortResult +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "status", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(SessionsClientMetadataEntryOk), "ok")] +[JsonDerivedType(typeof(SessionsClientMetadataEntryNotFound), "notFound")] +[JsonDerivedType(typeof(SessionsClientMetadataEntryCorrupt), "corrupt")] +[JsonDerivedType(typeof(SessionsClientMetadataEntryUnsupportedVersion), "unsupportedVersion")] +[JsonDerivedType(typeof(SessionsClientMetadataEntryUnavailable), "unavailable")] +public partial class SessionsClientMetadataEntry { - /// Error message if the abort failed. - [JsonPropertyName("error")] - public string? Error { get; set; } - - /// Whether the abort completed successfully. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// The type discriminator. + [JsonPropertyName("status")] + public virtual string Status { get; set; } = string.Empty; } -/// Parameters for aborting the current turn. + +/// The ok variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AbortRequest +public partial class SessionsClientMetadataEntryOk : SessionsClientMetadataEntry { - /// Finite reason code describing why the current turn was aborted. - [JsonPropertyName("reason")] - public AbortReason? Reason { get; set; } + /// + [JsonIgnore] + public override string Status => "ok"; - /// Target session identifier. + /// Validated client metadata, possibly empty or projected to requested keys. + [JsonPropertyName("metadata")] + public required IDictionary Metadata { get; set; } + + /// Requested session ID. [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + public required string SessionId { get; set; } } -/// Result of interrupting the main agent turn. +/// The notFound variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class InterruptMainTurnResult +public partial class SessionsClientMetadataEntryNotFound : SessionsClientMetadataEntry { - /// Whether an in-flight main agent turn was interrupted. False when the main loop was not processing. - [JsonPropertyName("interrupted")] - public bool Interrupted { get; set; } + /// + [JsonIgnore] + public override string Status => "notFound"; + + /// Requested session ID. + [JsonPropertyName("sessionId")] + public required string SessionId { get; set; } } -/// Parameters for interrupting the main agent turn. +/// The corrupt variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class InterruptMainTurnRequest +public partial class SessionsClientMetadataEntryCorrupt : SessionsClientMetadataEntry { - /// When true, the user's queued prompts are preserved and run as the next turn once the interrupted turn unwinds; when false (the default), the queue is cleared like a plain abort. - [JsonPropertyName("flushQueued")] - public bool? FlushQueued { get; set; } + /// + [JsonIgnore] + public override string Status => "corrupt"; - /// Target session identifier. + /// Requested session ID. [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + public required string SessionId { get; set; } } -/// Identifies the target session. +/// The unsupportedVersion variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionCancelAllBackgroundAgentsRequest +public partial class SessionsClientMetadataEntryUnsupportedVersion : SessionsClientMetadataEntry { - /// Target session identifier. + /// + [JsonIgnore] + public override string Status => "unsupportedVersion"; + + /// Requested session ID. [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + public required string SessionId { get; set; } } -/// Parameters for shutting down the session. +/// The unavailable variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ShutdownRequest +public partial class SessionsClientMetadataEntryUnavailable : SessionsClientMetadataEntry { - /// Dispatch deferred sessionEnd hooks in the background with their full per-hook timeoutSec instead of awaiting them under the short shared shutdown budget. Set this when the host process keeps running after the session closes (for example the CLI's /clear), so a slow hook neither blocks the close nor is aborted. Hooks still detached when the process later exits are terminated with it. Defaults to false. - [JsonPropertyName("detachSessionEndHooks")] - public bool? DetachSessionEndHooks { get; set; } + /// + [JsonIgnore] + public override string Status => "unavailable"; - /// Optional human-readable reason. Typically the message of the error that triggered shutdown when type is 'error'. - [JsonPropertyName("reason")] - public string? Reason { get; set; } + /// Filesystem or provider error code. Clients should not assume every provider uses operating-system error codes. + [JsonPropertyName("code")] + public required string Code { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Human-readable diagnostic message. Not stable for programmatic matching. + [JsonPropertyName("message")] + public required string Message { get; set; } - /// Why the session is being shut down. Defaults to "routine" when omitted. - [JsonPropertyName("type")] - public ShutdownType? Type { get; set; } + /// Requested session ID. + [JsonPropertyName("sessionId")] + public required string SessionId { get; set; } } -/// Identifier of the session event that was emitted for the log message. +/// Bounded batch request for client-owned metadata from persisted local sessions. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class LogResult +internal sealed class SessionsGetClientMetadataRequest { - /// The unique identifier of the emitted session event. - [JsonPropertyName("eventId")] - public Guid EventId { get; set; } + /// Case-sensitive keys to project from each valid bag. Each key must be non-empty, at most 256 UTF-8 bytes, and outside the reserved `copilot/` and `github/` namespaces. Omit to return every entry. + [JsonPropertyName("keys")] + public IList? Keys { get; set; } + + /// Session IDs to inspect. Results preserve this order. + [JsonPropertyName("sessionIds")] + public IList SessionIds { get => field ??= []; set; } } -/// Message text, optional severity level, persistence flag, optional follow-up URL, and optional tip. +/// Batch of session events returned by a read, with cursor and continuation metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class LogRequest +public sealed class EventsReadResult { - /// When true, the message is transient and not persisted to the session event log on disk. - [JsonPropertyName("ephemeral")] - public bool? Ephemeral { get; set; } - - /// Log severity level. Determines how the message is displayed in the timeline. Defaults to "info". - [JsonPropertyName("level")] - public SessionLogLevel? Level { get; set; } - - /// Human-readable message. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Opaque cursor for the next read. Pass back unchanged in the next read.cursor to continue from where this read left off. Always present, even when no events were returned. For a backward read this cursor pages toward OLDER events; keep passing `direction: backward` with it (the cursor is also self-describing, so backward paging continues correctly). + [JsonPropertyName("cursor")] + public string Cursor { get; set; } = string.Empty; - /// Optional actionable tip displayed alongside the message. Only honored on `level: "info"`. - [JsonPropertyName("tip")] - public string? Tip { get; set; } + /// Cursor status: 'ok' means the cursor was applied successfully. For session.eventLog.read, 'expired' means the cursor referred to an event that no longer exists in active history and the read fell back to a boundary of the remaining history: the beginning for a forward read or the newest window for a backward read. That fallback may overlap already rendered events, so active-session consumers should reset, rebase, or deduplicate before continuing. sessions.readPersistedEvents has stricter snapshot semantics: 'expired' returns an empty terminal page and never switches to a replacement journal generation. Other persisted-read I/O failures are RPC errors with diagnostics, not cursor expiry. + [JsonPropertyName("cursorStatus")] + public EventsCursorStatus CursorStatus { get; set; } - /// Domain category for this log entry (e.g., "mcp", "subscription", "policy", "model"). Maps to `infoType`/`warningType`/`errorType` on the emitted event. Defaults to "notification". - [JsonPropertyName("type")] - public string? Type { get; set; } + /// Session events for this batch, merged into a single stream in creation order: durable (persisted) events and ephemeral events interleave exactly as they were emitted. Set `includeEphemeral: false` to receive only durable events. Ephemeral events are never replayable once pruned from the in-memory ring, so a consumer that needs them should keep reading with a non-zero `waitMs`. For a backward (tail-first) read, the returned window contains persisted events only, still in chronological (oldest-to-newest) append order. + [JsonPropertyName("events")] + public IList Events { get => field ??= []; set; } - /// Optional URL the user can open in their browser for more details. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("url")] - public string? Url { get; set; } + /// True when more events are available in the read's direction. For a backward read, true means older persisted events remain before the returned window. A persisted-event page may contain fewer than `max` events because of its byte budget while still reporting hasMore true; continue according to this flag rather than the event count. + [JsonPropertyName("hasMore")] + public bool HasMore { get; set; } } -/// Adapter-declared policy that tells clients whether discovery may run automatically. +/// Pagination options for reading an inactive or active local session's persisted event journal. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderAutomaticDiscoveryPolicy +internal sealed class SessionsReadPersistedEventsRequest { - /// Whether automatic discovery is allowed, limited to configured providers, or explicit-only. - [JsonPropertyName("mode")] - public ModelProviderAutomaticDiscoveryMode Mode { get; set; } + /// Opaque, process-local, single-use cursor returned by the previous persisted-event read. Omit on the first call and issue continuations sequentially; reusing the same cursor returns an expired terminal page. + [JsonPropertyName("cursor")] + public string? Cursor { get; set; } - /// Maximum network scope used by this adapter during discovery. - [JsonPropertyName("networkScope")] - public ModelProviderDiscoveryNetworkScope NetworkScope { get; set; } + /// Direction to page through persisted history. Forward starts at the beginning; backward starts with the newest events. Events in each page remain chronological. This selects the initial read only; a continuation always uses the direction bound into its cursor. + [JsonPropertyName("direction")] + public EventsReadDirection? Direction { get; set; } - /// True when discovery requires non-null caller input. Omission or null is rejected before adapter execution. When false, omitted or null input selects adapter defaults without schema validation. - [JsonPropertyName("requiresInput")] - public bool RequiresInput { get; set; } + /// Maximum number of events to return in this batch (1–1000, default 200). Pages may contain fewer events to keep the serialized event array within a soft 1 MiB budget including resolved binary assets; one oversized event is returned alone to guarantee progress. + [JsonPropertyName("max")] + public long? Max { get; set; } - /// True when the adapter must be enabled by a trusted owner, such as a trusted extension, before automatic discovery may run. - [JsonPropertyName("requiresTrust")] - public bool RequiresTrust { get; set; } + /// Session ID whose persisted event journal should be read. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// An operation supported by a model-provider adapter. +/// Recent local session IDs that contain user-visible history. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderAdapterOperationDescriptor +internal sealed class SessionsListNonEmptySessionIdsResult { - /// Optional self-contained JSON Schema Draft 7 for non-null discovery input. Only supported on discover. No external references are resolved. Omitted or null input selects defaults when requiresInput is false. Without a schema, the adapter validates supplied input. - [JsonPropertyName("inputSchema")] - public JsonElement? InputSchema { get; set; } - - /// Supported operation name: `discover`, `getStatus`, or `models.list`. Unknown names and duplicate declarations are rejected. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Session IDs ordered newest-first. + [JsonPropertyName("sessionIds")] + public IList SessionIds { get => field ??= []; set; } } -/// Contributor attribution, independent of routing identity and authorization. +/// Limit for non-empty local session IDs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderAttribution +internal sealed class SessionsListNonEmptySessionIdsRequest { - /// Human-readable contributor name, not the adapter display name. - [JsonPropertyName("ownerDisplayName")] - public string? OwnerDisplayName { get; set; } - - /// Stable contributor identifier. Required and nonblank for extension and custom sources; optional for built-in and configured sources. Does not grant authority. - [JsonPropertyName("ownerId")] - public string? OwnerId { get; set; } - - /// Kind of component that supplied the adapter. Attribution does not confer authority. - [JsonPropertyName("source")] - public ModelProviderProvenanceSource Source { get; set; } + /// Maximum number of session IDs to return. + [JsonPropertyName("limit")] + public long? Limit { get; set; } } -/// A normalized model-provider adapter in the session's effective catalog. +/// ID of the local session bound to the given GitHub task, or omitted when none. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderAdapterDescriptor +public sealed class SessionsFindByTaskIDResult { - /// Stable opaque identity for routing to this adapter. Unique in the effective catalog, independent of live registration generations. - [JsonPropertyName("adapterId")] - public string AdapterId { get; set; } = string.Empty; + /// Omitted when no local session is bound to that GitHub task. + [JsonPropertyName("sessionId")] + public string? SessionId { get; set; } +} - /// Adapter-declared policy for passive and automatic discovery. - [JsonPropertyName("automaticDiscovery")] - public ModelProviderAutomaticDiscoveryPolicy AutomaticDiscovery { get => field ??= new(); set; } +/// GitHub task ID to look up. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionsFindByTaskIDRequest +{ + /// GitHub task ID to look up. + [JsonPropertyName("taskId")] + public string TaskId { get; set; } = string.Empty; +} - /// Human-readable provider name. - [JsonPropertyName("displayName")] - public string DisplayName { get; set; } = string.Empty; +/// Session ID matching the prefix, omitted when no unique match exists. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionsFindByPrefixResult +{ + /// Omitted when no unique session matches the prefix (no match or ambiguous). + [JsonPropertyName("sessionId")] + public string? SessionId { get; set; } +} - /// Operations supported by this provider adapter. - [JsonPropertyName("operations")] - public IList Operations { get => field ??= []; set; } +/// UUID prefix to resolve to a unique session ID. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionsFindByPrefixRequest +{ + /// UUID prefix (>=7 hex chars, <36 chars). Returns the unique session ID, or undefined when there is no match or the prefix matches multiple sessions. + [JsonPropertyName("prefix")] + public string Prefix { get; set; } = string.Empty; +} - /// Attribution for the adapter itself. - [JsonPropertyName("provenance")] - public ModelProviderAttribution Provenance { get => field ??= new(); set; } +/// Most-relevant session ID for the supplied context, or omitted when no sessions exist. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionsGetLastForContextResult +{ + /// Most-relevant session ID for the supplied context, or omitted when no sessions exist. + [JsonPropertyName("sessionId")] + public string? SessionId { get; set; } +} - /// Descriptive provider family, such as `ollama`. Different adapters may have the same family; use adapterId for routing. - [JsonPropertyName("providerKind")] - public string ProviderKind { get; set; } = string.Empty; +/// Optional working-directory context used to score session relevance. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionsGetLastForContextRequest +{ + /// Optional working-directory context used to score session relevance. When omitted the most-recently-modified session wins. + [JsonPropertyName("context")] + public SessionContext? Context { get; set; } } -/// Normalized model-provider adapter definitions available to the session, not discovered instances. +/// Absolute path to the session's events.jsonl file on disk. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderAdapterCatalog +internal sealed class SessionsGetEventFilePathResult { - /// Available provider adapters ordered by adapterId. - [JsonPropertyName("providers")] - public IList Providers { get => field ??= []; set; } + /// Absolute path to the session's events.jsonl file. + [JsonPropertyName("filePath")] + public string FilePath { get; set; } = string.Empty; } -/// Identifies the target session. +/// Session ID whose event-log file path to compute. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionProvidersGetCatalogRequest +internal sealed class SessionsGetEventFilePathRequest { - /// Target session identifier. + /// Session ID whose event-log file path to compute. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Attribution for the adapter that produced a provider row. +/// Map of sessionId -> on-disk size in bytes for each session's workspace directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderProvenance +public sealed class SessionSizes { - /// Stable opaque adapter identity from the effective catalog. Treat this as a whole identifier, not a parseable owner or kind. - [JsonPropertyName("adapterId")] - public string AdapterId { get; set; } = string.Empty; - - /// Human-readable contributor name, not the adapter display name. - [JsonPropertyName("ownerDisplayName")] - public string? OwnerDisplayName { get; set; } - - /// Stable contributor identifier when the adapter has an owner outside the runtime. Independent of the contribution mechanism and not a routing key. - [JsonPropertyName("ownerId")] - public string? OwnerId { get; set; } - - /// Descriptive provider family that produced this row; not a routing key. - [JsonPropertyName("providerKind")] - public string ProviderKind { get; set; } = string.Empty; - - /// Kind of component that supplied the adapter. - [JsonPropertyName("source")] - public ModelProviderProvenanceSource Source { get; set; } + /// Map of sessionId -> on-disk size in bytes for the session's workspace directory. + [JsonPropertyName("sizes")] + public IDictionary Sizes { get => field ??= new Dictionary(); set; } } -/// Serializable reference to a discovered provider instance. +/// Session IDs from the input set that are currently in use by another process. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderInstanceReference +public sealed class SessionsCheckInUseResult { - /// Stable opaque identity of the adapter that owns this reference. Must be present in the target session's effective catalog. - [JsonPropertyName("adapterId")] - public string AdapterId { get; set; } = string.Empty; - - /// Stable instance identifier derived by the provider adapter, such as `ollama:{normalizedEndpoint}`. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; - - /// Absolute provider management URI. The adapter validates normalization, supported schemes, and permission to access it against its bound configuration; a reference does not grant authority. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("managementEndpoint")] - public string ManagementEndpoint { get; set; } = string.Empty; - - /// Descriptive provider family. Must match the selected adapter; not a routing key. - [JsonPropertyName("providerKind")] - public string ProviderKind { get; set; } = string.Empty; + /// Session IDs from the input set that are currently held by another running process via an alive lock file. + [JsonPropertyName("inUse")] + public IList InUse { get => field ??= []; set; } } -/// A normalized model-provider instance discovered by the runtime. +/// Session IDs to test for live in-use locks. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderInstance +internal sealed class SessionsCheckInUseRequest { - /// Human-readable instance name. - [JsonPropertyName("displayName")] - public string DisplayName { get; set; } = string.Empty; - - /// Inference API endpoint when the provider exposes one separately from its management endpoint. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("inferenceEndpoint")] - public string? InferenceEndpoint { get; set; } - - /// Transport to use for inference against this instance. - [JsonPropertyName("inferenceTransport")] - public ProviderEndpointTransport? InferenceTransport { get; set; } + /// Session IDs to test for live in-use locks. + [JsonPropertyName("sessionIds")] + public IList SessionIds { get => field ??= []; set; } +} - /// Provider family to use for inference against this instance. - [JsonPropertyName("inferenceType")] - public ProviderEndpointType? InferenceType { get; set; } +/// The session's persisted remote-steerable flag, or omitted when no value has been persisted. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionsGetPersistedRemoteSteerableResult +{ + /// The session's persisted remote-steerable flag if recorded; omitted when no value has been persisted. + [JsonPropertyName("remoteSteerable")] + public bool? RemoteSteerable { get; set; } +} - /// Wire API to use for inference against this instance, when required by the provider family. - [JsonPropertyName("inferenceWireApi")] - public ProviderEndpointWireApi? InferenceWireApi { get; set; } +/// Session ID to look up the persisted remote-steerable flag for. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionsGetPersistedRemoteSteerableRequest +{ + /// Session ID to look up the persisted remote-steerable flag for. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Attribution for the adapter that produced this instance. - [JsonPropertyName("provenance")] - public ModelProviderProvenance Provenance { get => field ??= new(); set; } +/// 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. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionsCloseResult +{ +} - /// Self-contained reference for subsequent provider operations. - [JsonPropertyName("reference")] - public ModelProviderInstanceReference Reference { get => field ??= new(); set; } +/// Session ID to close. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionsCloseRequest +{ + /// Session ID to close. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Typed provider-operation outcome. Use the code for control flow and the optional message for display. +/// Map of sessionId -> bytes freed by removing the session's workspace directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderOperationOutcome +public sealed class SessionBulkDeleteResult { - /// Machine-readable operation outcome. - [JsonPropertyName("code")] - public ModelProviderOperationOutcomeCode Code { get; set; } + /// Map of sessionId -> bytes freed by removing the session's workspace directory. Sessions whose deletion failed are omitted from this map (failures are logged on the server but not surfaced per-id; check the map for absent IDs to detect them). + [JsonPropertyName("freedBytes")] + public IDictionary FreedBytes { get => field ??= new Dictionary(); set; } +} - /// Human-readable detail for non-success outcomes. - [JsonPropertyName("message")] - public string? Message { get; set; } +/// Session IDs to close, deactivate, and delete from disk. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionsBulkDeleteRequest +{ + /// Session IDs to close, deactivate, and delete from disk. + [JsonPropertyName("sessionIds")] + public IList SessionIds { get => field ??= []; set; } } -/// Provider instances found by a discovery operation. +/// Session ID to delete from disk. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderDiscoverResult +internal sealed class SessionsDeleteRequest { - /// Discovered provider instances. Empty when passive default discovery finds no reachable provider. - [JsonPropertyName("instances")] - public IList Instances { get => field ??= []; set; } + /// Session ID to delete. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Typed operation outcome. Passive discovery can return `absent` with an empty instance list. - [JsonPropertyName("outcome")] - public ModelProviderOperationOutcome Outcome { get => field ??= new(); set; } + /// Internal resolved session directory path to delete. + [JsonPropertyName("sessionPath")] + public string? SessionPath { get; set; } } -/// Provider discovery parameters. +/// Outcome of the prune operation: deleted IDs, dry-run candidates, skipped IDs, total bytes freed, and the dry-run flag. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelProviderDiscoverRequest +public sealed class SessionPruneResult { - /// Opaque adapter identity returned by `session.providers.getCatalog`. - [JsonPropertyName("adapterId")] - public string AdapterId { get; set; } = string.Empty; + /// Session IDs that would be deleted in dry-run mode (always empty otherwise). + [JsonPropertyName("candidates")] + public IList Candidates { get => field ??= []; set; } - /// Provider-specific JSON input. Omission or null selects adapter defaults unless requiresInput is true. Non-null input is validated against the advertised Draft 7 schema when present; otherwise validation belongs to the adapter. - [JsonPropertyName("input")] - public JsonElement? Input { get; set; } + /// Session IDs that were deleted (always empty in dry-run mode). + [JsonPropertyName("deleted")] + public IList Deleted { get => field ??= []; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// True when no deletions were actually performed. + [JsonPropertyName("dryRun")] + public bool DryRun { get; set; } + + /// Total bytes freed (actual when not dry-run, projected when dry-run). + [JsonPropertyName("freedBytes")] + public long FreedBytes { get; set; } + + /// Session IDs that were skipped (e.g., named sessions). + [JsonPropertyName("skipped")] + public IList Skipped { get => field ??= []; set; } } -/// Current health information for a provider instance. +/// Age threshold and optional flags controlling which old sessions are pruned (or simulated when dryRun is true). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderStatus +internal sealed class SessionsPruneOldRequest { - /// Normalized provider instance. - [JsonPropertyName("instance")] - public ModelProviderInstance Instance { get => field ??= new(); set; } + /// When true, only report what would be deleted without performing any deletion. + [JsonPropertyName("dryRun")] + public bool? DryRun { get; set; } - /// Typed operation outcome. - [JsonPropertyName("outcome")] - public ModelProviderOperationOutcome Outcome { get => field ??= new(); set; } + /// Session IDs that should never be considered for pruning. + [JsonPropertyName("excludeSessionIds")] + public IList? ExcludeSessionIds { get; set; } - /// Open provider status value, such as `healthy`, `unreachable`, or `notInstalled`. - [JsonPropertyName("status")] - public string Status { get; set; } = string.Empty; + /// When true, named sessions (set via /rename) are also eligible for pruning. + [JsonPropertyName("includeNamed")] + public bool? IncludeNamed { get; set; } - /// Provider-reported version. - [JsonPropertyName("version")] - public string? Version { get; set; } + /// Delete sessions whose modifiedTime is at least this many days old. + [JsonPropertyName("olderThanDays")] + public long OlderThanDays { get; set; } } -/// Provider status request parameters. +/// Flush a session's pending events to disk. No-op when no writer exists for the session (e.g., already closed). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelProviderGetStatusRequest +public sealed class SessionsSaveResult { - /// Provider instance reference returned by discovery. - [JsonPropertyName("instance")] - public ModelProviderInstanceReference Instance { get => field ??= new(); set; } +} - /// Target session identifier. +/// Session ID whose pending events should be flushed to disk. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionsSaveRequest +{ + /// Session ID whose pending events should be flushed to disk. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Provider-reported model artifact metadata. +/// 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. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelArtifactDetails +public sealed class SessionsReleaseLockResult { - /// Provider-reported model architecture. - [JsonPropertyName("architecture")] - public string? Architecture { get; set; } - - /// Provider-reported model families. - [JsonPropertyName("families")] - public IList? Families { get; set; } - - /// Primary model family. - [JsonPropertyName("family")] - public string? Family { get; set; } - - /// Artifact format, such as `gguf`. - [JsonPropertyName("format")] - public string? Format { get; set; } - - /// Provider-reported parameter count label. - [JsonPropertyName("parameterSize")] - public string? ParameterSize { get; set; } +} - /// Provider-reported quantization label. - [JsonPropertyName("quantization")] - public string? Quantization { get; set; } +/// Session ID whose in-use lock should be released. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionsReleaseLockRequest +{ + /// Session ID whose in-use lock should be released. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Provider-reported tokenizer. - [JsonPropertyName("tokenizer")] - public string? Tokenizer { get; set; } +/// The enriched metadata records, with summary and context fields backfilled where available. Sessions confirmed empty and unnamed are omitted. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionEnrichMetadataResult +{ + /// Enriched records, with summary and context backfilled. Sessions confirmed empty and unnamed may be omitted. + [JsonPropertyName("sessions")] + public IList Sessions { get => field ??= []; set; } } -/// A non-fatal provider observation warning. +/// Session metadata records to enrich with summary and context information. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderWarning +internal sealed class SessionsEnrichMetadataRequest { - /// Machine-readable warning code. - [JsonPropertyName("code")] - public string Code { get; set; } = string.Empty; + /// Session metadata records to enrich. Records that already have summary and context are returned unchanged. + [JsonPropertyName("sessions")] + public IList Sessions { get => field ??= []; set; } +} - /// Human-readable warning message. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; +/// The workspace record that was written. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionsCreateWorkspaceResult +{ + /// The created workspace record, as JSON. + [JsonPropertyName("workspaceJson")] + public string WorkspaceJson { get; set; } = string.Empty; } -/// A model offered for agent conversations. Missing capability metadata does not disqualify a candidate. Models known to be incompatible, such as embedding-only models, are excluded by the adapter. +/// A working-directory context together with the client that produced it. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DiscoveredModel +public sealed class SessionWorkingDirectoryContextWithClient { - /// Provider-reported model capabilities. Omitted capability fields are unknown; explicit false values are preserved. - [JsonPropertyName("capabilities")] - public ModelCapabilities Capabilities { get => field ??= new(); set; } + /// Merge-base commit SHA. + [JsonPropertyName("baseCommit")] + public string? BaseCommit { get; set; } - /// Provider-reported model artifact details. - [JsonPropertyName("details")] - public ModelArtifactDetails Details { get => field ??= new(); set; } + /// Current git branch name. + [JsonPropertyName("branch")] + public string? Branch { get; set; } - /// Provider-reported artifact digest. - [JsonPropertyName("digest")] - public string? Digest { get; set; } + /// Name of the client that created the session. + [JsonPropertyName("clientName")] + public string? ClientName { get; set; } - /// Provider-native model identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Current working directory path. + [JsonPropertyName("cwd")] + public string Cwd { get; set; } = string.Empty; - /// Provider-reported last-modified timestamp. - [JsonPropertyName("modifiedAt")] - public DateTimeOffset? ModifiedAt { get; set; } + /// Root directory of the git repository. + [JsonPropertyName("gitRoot")] + public string? GitRoot { get; set; } - /// Provider-reported display name. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// Head commit of the current git branch. + [JsonPropertyName("headCommit")] + public string? HeadCommit { get; set; } - /// Attribution for the adapter that produced this model row. - [JsonPropertyName("provenance")] - public ModelProviderProvenance Provenance { get => field ??= new(); set; } + /// Hosting platform type of the repository. + [JsonPropertyName("hostType")] + public string? HostType { get; set; } - /// Provider-reported artifact size in bytes. - [JsonPropertyName("sizeBytes")] - public long? SizeBytes { get; set; } + /// Repository identifier derived from the git remote URL. + [JsonPropertyName("repository")] + public string? Repository { get; set; } - /// Non-fatal warnings encountered while enriching this model. - [JsonPropertyName("warnings")] - public IList Warnings { get => field ??= []; set; } + /// Raw host string from the git remote URL. + [JsonPropertyName("repositoryHost")] + public string? RepositoryHost { get; set; } } -/// Models offered for agent conversations by one provider instance. Adapters exclude known-incompatible models, but retain candidates with unknown capabilities. Listing does not guarantee compatibility. +/// Identity, state location and starting context for a workspace record. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DiscoveredModelList +internal sealed class SessionsCreateWorkspaceRequest { - /// Provider-native models in provider order. - [JsonPropertyName("models")] - public IList Models { get => field ??= []; set; } + /// Starting working-directory context. The record keeps `cwd`, `gitRoot`, `repository`, `hostType`, `branch`, and `clientName`. Other fields, including `repositoryHost`, `headCommit`, and `baseCommit`, are ignored. `hostType` must be `github` or `ado`. + [JsonPropertyName("context")] + public SessionWorkingDirectoryContextWithClient? Context { get; set; } - /// Typed operation outcome. - [JsonPropertyName("outcome")] - public ModelProviderOperationOutcome Outcome { get => field ??= new(); set; } -} + /// `windows` (any letter case) selects Windows path rules. Any other value selects POSIX path rules. + [JsonPropertyName("convention")] + public string Convention { get; set; } = string.Empty; -/// Provider model inventory request parameters. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelProviderModelsListRequest -{ - /// Provider instance reference returned by discovery. - [JsonPropertyName("instance")] - public ModelProviderInstanceReference Instance { get => field ??= new(); set; } + /// User-supplied display name for the workspace. + [JsonPropertyName("name")] + public string? Name { get; set; } - /// Target session identifier. + /// Session ID the workspace record belongs to. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Directory the session's state is written under when no session filesystem provider is configured. Ignored when a provider is configured; the provider's session state path is used instead. + [JsonPropertyName("sessionStatePath")] + public string SessionStatePath { get; set; } = string.Empty; } -/// Vision-specific limits. +/// The workspace record on disk, omitted when the session has none. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelCapabilitiesOverrideLimitsVision +internal sealed class SessionsLoadWorkspaceResult { - /// Maximum image size in bytes. - [JsonPropertyName("max_prompt_image_size")] - public long? MaxPromptImageSize { get; set; } - - /// Maximum number of images per prompt. - [JsonPropertyName("max_prompt_images")] - public long? MaxPromptImages { get; set; } - - /// MIME types the model accepts. - [JsonPropertyName("supported_media_types")] - public IList? SupportedMediaTypes { get; set; } + /// The workspace record, as JSON. Omitted when the record does not exist. + [JsonPropertyName("workspaceJson")] + public string? WorkspaceJson { get; set; } } -/// Token limits for prompts, outputs, and context window. +/// Where the session's state lives, as a root directory and the session ID under it. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelCapabilitiesOverrideLimits +internal sealed class SessionsLoadWorkspaceRequest { - /// Maximum total context window size in tokens. - [JsonPropertyName("max_context_window_tokens")] - public long? MaxContextWindowTokens { get; set; } - - /// Maximum number of output/completion tokens. - [JsonPropertyName("max_output_tokens")] - public long? MaxOutputTokens { get; set; } - - /// Maximum number of prompt/input tokens. - [JsonPropertyName("max_prompt_tokens")] - public long? MaxPromptTokens { get; set; } + /// Session ID naming the state directory under the sessions home. Rejected when it is absolute or contains a parent component, so it cannot escape the sessions home. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Vision-specific limits. - [JsonPropertyName("vision")] - public ModelCapabilitiesOverrideLimitsVision? Vision { get; set; } + /// Root directory every session's state directory sits under. + [JsonPropertyName("sessionsHome")] + public string SessionsHome { get; set; } = string.Empty; } -/// Feature flags indicating what the model supports. +/// The merge completed. The record carries the supplied workspace-schema fields, but a stored `fork_count` stays. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelCapabilitiesOverrideSupports +internal sealed class SessionsUpdateWorkspaceFieldsResult { - /// Resolved Anthropic adaptive-thinking capability — unsupported / optional / required / adaptive_only. 'required' models reject thinking.type='enabled' with HTTP 400 but still accept 'disabled' (e.g. opus-4.7/4.8/5, sonnet-5); 'adaptive_only' models accept nothing but 'adaptive' (e.g. fable, mythos). - [JsonPropertyName("adaptive_thinking")] - public AdaptiveThinkingSupport? AdaptiveThinking { get; set; } +} - /// Whether this model supports reasoning effort configuration. - [JsonPropertyName("reasoningEffort")] - public bool? ReasoningEffort { get; set; } +/// Where the session's state lives, plus workspace-schema fields to merge into its workspace record. Stored keys outside the schema are not preserved, and a stored `fork_count` is never replaced. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionsUpdateWorkspaceFieldsRequest +{ + /// Workspace-schema fields to merge into the record, as a JSON object. Fields the object omits keep their stored values, except stored keys outside the schema are not preserved and a stored `fork_count` is never replaced. + [JsonPropertyName("fieldsJson")] + public string FieldsJson { get; set; } = string.Empty; - /// Whether this model supports canonical tool calling. - [JsonPropertyName("toolCalls")] - public bool? ToolCalls { get; set; } + /// Session ID naming the state directory under the sessions home. Rejected when it is absolute or contains a parent component, so it cannot escape the sessions home. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Whether this model supports vision/image input. - [JsonPropertyName("vision")] - public bool? Vision { get; set; } + /// Root directory every session's state directory sits under. + [JsonPropertyName("sessionsHome")] + public string SessionsHome { get; set; } = string.Empty; } -/// Optional capability overrides (vision, tool_calls, reasoning, etc.). +/// 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. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelCapabilitiesOverride +public sealed class SessionsReloadPluginHooksResult { - /// Token limits for prompts, outputs, and context window. - [JsonPropertyName("limits")] - public ModelCapabilitiesOverrideLimits? Limits { get; set; } - - /// Feature flags indicating what the model supports. - [JsonPropertyName("supports")] - public ModelCapabilitiesOverrideSupports? Supports { get; set; } } -/// RPC data type for ProtocolSystemMessageAppendConfig operations. +/// Active session ID and an optional flag for deferring repo-level hooks until folder trust. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProtocolSystemMessageAppendConfig +internal sealed class SessionsReloadPluginHooksRequest { - /// Text appended to the standard system prompt. - [JsonPropertyName("content")] - public string? Content { get; set; } + /// When true, skip repo-level hooks. Use before folder trust is confirmed; loadDeferredRepoHooks loads them post-trust. + [JsonPropertyName("deferRepoHooks")] + public bool? DeferRepoHooks { get; set; } - /// Append-mode discriminator. Omission also selects append mode. - [JsonPropertyName("mode")] - public ProtocolAppendMode? Mode { get; set; } + /// Active session ID to reload hooks for. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// RPC data type for SystemMessageBlock operations. +/// Queued repo-level startup prompts and the total hook command count after loading. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SystemMessageBlock +public sealed class SessionLoadDeferredRepoHooksResult { - /// Whether providers with explicit prompt caching should place a cache breakpoint after this block. - [JsonPropertyName("cacheBreakpoint")] - public bool? CacheBreakpoint { get; set; } - - /// Text content for this system-message block. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// Total hook command count (user + plugin + repo) loaded for the session by this call. Captured atomically with startupPrompts so callers don't need to read a separate counter. + [JsonPropertyName("hookCount")] + public long HookCount { get; set; } - /// Whether the block is static and may be cached independently of dynamic prompt content. - [JsonPropertyName("isStatic")] - public bool? IsStatic { get; set; } + /// Repo-level startup prompts queued from repo hook configs. Empty on resume, when no repo configs were pending, or when disableAllHooks is set. + [JsonPropertyName("startupPrompts")] + public IList StartupPrompts { get => field ??= []; set; } } -/// RPC data type for ProtocolSystemMessageReplaceConfig operations. +/// Active session ID whose deferred repo-level hooks should be loaded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProtocolSystemMessageReplaceConfig +internal sealed class SessionsLoadDeferredRepoHooksRequest { - /// Complete replacement system-message text. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// Active session ID whose deferred repo-level hooks should be loaded. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Optional structured blocks corresponding to the replacement content. - [JsonPropertyName("contentBlocks")] - public IList? ContentBlocks { get; set; } +/// 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. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionsSetAdditionalPluginsResult +{ +} - /// Replace-mode discriminator. - [JsonPropertyName("mode")] - public ProtocolReplaceMode Mode { get; set; } +/// Manager-wide additional plugins to register; replaces any previously-configured set. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionsSetAdditionalPluginsRequest +{ + /// Manager-wide additional plugins to register. Replaces any previously-configured set. Pass an empty array to clear. + [JsonPropertyName("plugins")] + public IList Plugins { get => field ??= []; set; } } -/// RPC data type for ProtocolStaticSectionOverride operations. +/// Dynamic-context board entry count, when available. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProtocolStaticSectionOverride +internal sealed class SessionsGetBoardEntryCountResult { - /// Declarative operation applied to the section. - [JsonPropertyName("action")] - public ProtocolStaticSectionAction Action { get; set; } + /// Board entry count, when available. + [JsonPropertyName("count")] + public long? Count { get; set; } +} - /// Optional content used by replace, append, and prepend operations. - [JsonPropertyName("content")] - public string? Content { get; set; } +/// Session ID whose board entry count should be returned. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionsGetBoardEntryCountRequest +{ + /// Session ID whose board entry count should be returned. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Polymorphic base type discriminated by action. +/// State of the runtime-managed remote-control singleton. +/// Polymorphic base type discriminated by state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonPolymorphic( - TypeDiscriminatorPropertyName = "action", + TypeDiscriminatorPropertyName = "state", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(ProtocolMarkerSectionOverrideTransform), "transform")] -[JsonDerivedType(typeof(ProtocolMarkerSectionOverridePreserve), "preserve")] -public partial class ProtocolMarkerSectionOverride +[JsonDerivedType(typeof(RemoteControlStatusOff), "off")] +[JsonDerivedType(typeof(RemoteControlStatusConnecting), "connecting")] +[JsonDerivedType(typeof(RemoteControlStatusActive), "active")] +[JsonDerivedType(typeof(RemoteControlStatusError), "error")] +public partial class RemoteControlStatus { /// The type discriminator. - [JsonPropertyName("action")] - public virtual string Action { get; set; } = string.Empty; + [JsonPropertyName("state")] + public virtual string State { get; set; } = string.Empty; } -/// The transform variant of . +/// Remote control is not connected. +/// The off variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ProtocolMarkerSectionOverrideTransform : ProtocolMarkerSectionOverride +public partial class RemoteControlStatusOff : RemoteControlStatus { /// [JsonIgnore] - public override string Action => "transform"; + public override string State => "off"; } -/// The preserve variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ProtocolMarkerSectionOverridePreserve : ProtocolMarkerSectionOverride +/// Remote control is in the middle of initial setup. +/// The connecting variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class RemoteControlStatusConnecting : RemoteControlStatus { /// [JsonIgnore] - public override string Action => "preserve"; -} - -/// JSON union data type for ProtocolSectionOverride. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -public sealed partial class ProtocolSectionOverride -{ - /// Gets the value when this instance contains . - public ProtocolStaticSectionOverride? ProtocolStaticSectionOverride { get; } - - /// Gets the value when this instance contains . - public ProtocolMarkerSectionOverride? ProtocolMarkerSectionOverride { get; } - - /// Initializes a new instance of the class from . - public ProtocolSectionOverride(ProtocolStaticSectionOverride value) - { - ArgumentNullException.ThrowIfNull(value); - ProtocolStaticSectionOverride = value; - } - - /// Converts to . - public static implicit operator ProtocolSectionOverride(ProtocolStaticSectionOverride value) => new(value); - - /// Initializes a new instance of the class from . - public ProtocolSectionOverride(ProtocolMarkerSectionOverride value) - { - ArgumentNullException.ThrowIfNull(value); - ProtocolMarkerSectionOverride = value; - } - - /// Converts to . - public static implicit operator ProtocolSectionOverride(ProtocolMarkerSectionOverride value) => new(value); - - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter - { - /// - public override ProtocolSectionOverride Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (reader.TokenType == JsonTokenType.Null) - { - throw new JsonException("Expected JSON object for ProtocolSectionOverride."); - } - - using var document = JsonDocument.ParseValue(ref reader); - var element = document.RootElement; - if (element.ValueKind == JsonValueKind.Object && (element.TryGetProperty("action", out _) && element.GetProperty("action").ValueKind == JsonValueKind.String && (element.GetProperty("action").GetString() == "replace" || element.GetProperty("action").GetString() == "remove" || element.GetProperty("action").GetString() == "append" || element.GetProperty("action").GetString() == "prepend"))) - { - var protocolStaticSectionOverride = JsonSerializer.Deserialize(element, RpcJsonContext.Default.ProtocolStaticSectionOverride); - return protocolStaticSectionOverride is null ? throw new JsonException("Expected ProtocolStaticSectionOverride value.") : new ProtocolSectionOverride(protocolStaticSectionOverride); - } - if ((element.ValueKind == JsonValueKind.Object && (element.TryGetProperty("action", out _) && element.GetProperty("action").ValueKind == JsonValueKind.String && (element.GetProperty("action").GetString() == "transform")) || element.ValueKind == JsonValueKind.Object && (element.TryGetProperty("action", out _) && element.GetProperty("action").ValueKind == JsonValueKind.String && (element.GetProperty("action").GetString() == "preserve")))) - { - var protocolMarkerSectionOverride = JsonSerializer.Deserialize(element, RpcJsonContext.Default.ProtocolMarkerSectionOverride); - return protocolMarkerSectionOverride is null ? throw new JsonException("Expected ProtocolMarkerSectionOverride value.") : new ProtocolSectionOverride(protocolMarkerSectionOverride); - } - - throw new JsonException("JSON value did not match any ProtocolSectionOverride variant."); - } - - /// - public override void Write(Utf8JsonWriter writer, ProtocolSectionOverride value, JsonSerializerOptions options) - { - if (value.ProtocolStaticSectionOverride is { } protocolStaticSectionOverride) - { - JsonSerializer.Serialize(writer, protocolStaticSectionOverride, RpcJsonContext.Default.ProtocolStaticSectionOverride); - return; - } - if (value.ProtocolMarkerSectionOverride is { } protocolMarkerSectionOverride) - { - JsonSerializer.Serialize(writer, protocolMarkerSectionOverride, RpcJsonContext.Default.ProtocolMarkerSectionOverride); - return; - } - - throw new JsonException("No ProtocolSectionOverride variant value is set."); - } - } -} - -/// RPC data type for ProtocolSystemMessageCustomizeConfig operations. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProtocolSystemMessageCustomizeConfig -{ - /// Text appended after the customized sections. - [JsonPropertyName("content")] - public string? Content { get; set; } - - /// Customize-mode discriminator. - [JsonPropertyName("mode")] - public ProtocolCustomizeMode Mode { get; set; } + public override string State => "connecting"; - /// Named standard-prompt section overrides. - [JsonPropertyName("sections")] - public IDictionary? Sections { get; set; } + /// Session id the connection is attaching to. + [JsonPropertyName("attachedSessionId")] + public required string AttachedSessionId { get; set; } } -/// JSON union data type for ProtocolSystemMessageConfig. +/// Remote control is connected to a local session. +/// The active variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -public sealed partial class ProtocolSystemMessageConfig +public partial class RemoteControlStatusActive : RemoteControlStatus { - /// Gets the value when this instance contains . - public ProtocolSystemMessageAppendConfig? ProtocolSystemMessageAppendConfig { get; } - - /// Gets the value when this instance contains . - public ProtocolSystemMessageReplaceConfig? ProtocolSystemMessageReplaceConfig { get; } - - /// Gets the value when this instance contains . - public ProtocolSystemMessageCustomizeConfig? ProtocolSystemMessageCustomizeConfig { get; } - - /// Initializes a new instance of the class from . - public ProtocolSystemMessageConfig(ProtocolSystemMessageAppendConfig value) - { - ArgumentNullException.ThrowIfNull(value); - ProtocolSystemMessageAppendConfig = value; - } - - /// Converts to . - public static implicit operator ProtocolSystemMessageConfig(ProtocolSystemMessageAppendConfig value) => new(value); - - /// Initializes a new instance of the class from . - public ProtocolSystemMessageConfig(ProtocolSystemMessageReplaceConfig value) - { - ArgumentNullException.ThrowIfNull(value); - ProtocolSystemMessageReplaceConfig = value; - } - - /// Converts to . - public static implicit operator ProtocolSystemMessageConfig(ProtocolSystemMessageReplaceConfig value) => new(value); - - /// Initializes a new instance of the class from . - public ProtocolSystemMessageConfig(ProtocolSystemMessageCustomizeConfig value) - { - ArgumentNullException.ThrowIfNull(value); - ProtocolSystemMessageCustomizeConfig = value; - } - - /// Converts to . - public static implicit operator ProtocolSystemMessageConfig(ProtocolSystemMessageCustomizeConfig value) => new(value); - - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter - { - /// - public override ProtocolSystemMessageConfig Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (reader.TokenType == JsonTokenType.Null) - { - throw new JsonException("Expected JSON object for ProtocolSystemMessageConfig."); - } + /// + [JsonIgnore] + public override string State => "active"; - using var document = JsonDocument.ParseValue(ref reader); - var element = document.RootElement; - if (element.ValueKind == JsonValueKind.Object && (!element.TryGetProperty("mode", out _) || (element.TryGetProperty("mode", out _) && element.GetProperty("mode").ValueKind == JsonValueKind.String && (element.GetProperty("mode").GetString() == "append")))) - { - var protocolSystemMessageAppendConfig = JsonSerializer.Deserialize(element, RpcJsonContext.Default.ProtocolSystemMessageAppendConfig); - return protocolSystemMessageAppendConfig is null ? throw new JsonException("Expected ProtocolSystemMessageAppendConfig value.") : new ProtocolSystemMessageConfig(protocolSystemMessageAppendConfig); - } - if (element.ValueKind == JsonValueKind.Object && (element.TryGetProperty("mode", out _) && element.GetProperty("mode").ValueKind == JsonValueKind.String && (element.GetProperty("mode").GetString() == "replace"))) - { - var protocolSystemMessageReplaceConfig = JsonSerializer.Deserialize(element, RpcJsonContext.Default.ProtocolSystemMessageReplaceConfig); - return protocolSystemMessageReplaceConfig is null ? throw new JsonException("Expected ProtocolSystemMessageReplaceConfig value.") : new ProtocolSystemMessageConfig(protocolSystemMessageReplaceConfig); - } - if (element.ValueKind == JsonValueKind.Object && (element.TryGetProperty("mode", out _) && element.GetProperty("mode").ValueKind == JsonValueKind.String && (element.GetProperty("mode").GetString() == "customize"))) - { - var protocolSystemMessageCustomizeConfig = JsonSerializer.Deserialize(element, RpcJsonContext.Default.ProtocolSystemMessageCustomizeConfig); - return protocolSystemMessageCustomizeConfig is null ? throw new JsonException("Expected ProtocolSystemMessageCustomizeConfig value.") : new ProtocolSystemMessageConfig(protocolSystemMessageCustomizeConfig); - } + /// Session id remote control is pointed at. + [JsonPropertyName("attachedSessionId")] + public required string AttachedSessionId { get; set; } - throw new JsonException("JSON value did not match any ProtocolSystemMessageConfig variant."); - } + /// True while a read-only/session-sync export is deferred, awaiting the first `user.message` before its MC session exists. Marked internal: this field is excluded from the public SDK surface and is populated only on the CLI in-process path. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonInclude] + [JsonPropertyName("awaitingFirstMessage")] + internal bool? AwaitingFirstMessage { get; set; } - /// - public override void Write(Utf8JsonWriter writer, ProtocolSystemMessageConfig value, JsonSerializerOptions options) - { - if (value.ProtocolSystemMessageAppendConfig is { } protocolSystemMessageAppendConfig) - { - JsonSerializer.Serialize(writer, protocolSystemMessageAppendConfig, RpcJsonContext.Default.ProtocolSystemMessageAppendConfig); - return; - } - if (value.ProtocolSystemMessageReplaceConfig is { } protocolSystemMessageReplaceConfig) - { - JsonSerializer.Serialize(writer, protocolSystemMessageReplaceConfig, RpcJsonContext.Default.ProtocolSystemMessageReplaceConfig); - return; - } - if (value.ProtocolSystemMessageCustomizeConfig is { } protocolSystemMessageCustomizeConfig) - { - JsonSerializer.Serialize(writer, protocolSystemMessageCustomizeConfig, RpcJsonContext.Default.ProtocolSystemMessageCustomizeConfig); - return; - } + /// MC frontend URL for this session, when known. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("frontendUrl")] + public string? FrontendUrl { get; set; } - throw new JsonException("No ProtocolSystemMessageConfig variant value is set."); - } - } + /// Whether the MC session may steer this session. + [JsonPropertyName("isSteerable")] + public required bool IsSteerable { get; set; } } -/// A BYOK model definition referencing a named provider. +/// The last setup attempt failed. The singleton is otherwise off. +/// The error variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderModelConfig +public partial class RemoteControlStatusError : RemoteControlStatus { - /// Optional capability overrides (vision, tool_calls, reasoning, etc.). - [JsonPropertyName("capabilities")] - public ModelCapabilitiesOverride? Capabilities { get; set; } - - /// Provider-local model id, unique within its provider. The session-wide selection id (shown in the model list and passed to switchTo) is the provider-qualified `provider/id`. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; - - /// Maximum context window tokens for the model. - [JsonPropertyName("maxContextWindowTokens")] - public double? MaxContextWindowTokens { get; set; } - - /// Maximum output tokens for the model. - [JsonPropertyName("maxOutputTokens")] - public double? MaxOutputTokens { get; set; } - - /// Maximum prompt/input tokens for the model. - [JsonPropertyName("maxPromptTokens")] - public double? MaxPromptTokens { get; set; } - - /// Provider-published model metadata, preserved verbatim as the public Model.metadata object. - [JsonPropertyName("metadata")] - public IDictionary? Metadata { get; set; } - - /// Well-known base model id used for behavior/capability/config lookup. Defaults to `id`. - [JsonPropertyName("modelId")] - public string? ModelId { get; set; } - - /// Display name for model pickers. Defaults to the provider-qualified selection id (`provider/id`). - [JsonPropertyName("name")] - public string? Name { get; set; } - - /// Name of the configured provider that serves this model. - [JsonPropertyName("provider")] - public string Provider { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string State => "error"; - /// System-message configuration used when the runtime builds the standard prompt for this provider-qualified model, including general-purpose subagents. It uses the same object hierarchy as session-level systemMessage configuration, except transform actions are rejected because the current callback protocol is not model-scoped. When present, it overrides the session-wide configuration on those prompt paths. Selected custom-agent and specialized-subagent prompts remain authoritative. - [JsonPropertyName("systemMessage")] - public ProtocolSystemMessageConfig? SystemMessage { get; set; } + /// Session id the failing setup attempt targeted, when known. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("attachedSessionId")] + public string? AttachedSessionId { get; set; } - /// The model name sent to the provider API for inference. Defaults to `id`. - [JsonPropertyName("wireModel")] - public string? WireModel { get; set; } + /// Human-readable error message from the last setup attempt. + [JsonPropertyName("error")] + public required string Error { get; set; } } -/// Azure-specific provider options. +/// Wrapper for the singleton's current status. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderConfigAzure +public sealed class RemoteControlStatusResult { - /// API version. When set, uses the versioned deployment route. When omitted, uses the GA versionless v1 route. - [JsonPropertyName("apiVersion")] - public string? ApiVersion { get; set; } + /// State of the runtime-managed remote-control singleton. + [JsonPropertyName("status")] + public RemoteControlStatus Status { get => field ??= new(); set; } } -/// External SDK input for a named custom model provider. Ingested by the native protocol boundary before host dispatch. +/// Reattach to an existing MC session without creating a new one. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class NamedProviderConfig +public sealed class RemoteControlConfigExistingMcSession { - /// Static API key used to authenticate provider requests. - [JsonPropertyName("apiKey")] - public string? ApiKey { get; set; } - - /// Azure authentication configuration for the provider. - [JsonPropertyName("azure")] - public ProviderConfigAzure? Azure { get; set; } - - /// Base URL for provider API requests. - [JsonPropertyName("baseUrl")] - public string BaseUrl { get; set; } = string.Empty; - - /// Static bearer token used to authenticate provider requests. - [JsonPropertyName("bearerToken")] - public string? BearerToken { get; set; } - - /// Whether the host supplies bearer tokens dynamically. - [JsonPropertyName("hasBearerTokenProvider")] - public bool? HasBearerTokenProvider { get; set; } - - /// Additional HTTP headers included with provider requests. - [JsonPropertyName("headers")] - public IDictionary? Headers { get; set; } - - /// The product serving the provider's models, reported in telemetry as `model_provider`. Only affects telemetry. - [JsonPropertyName("modelProvider")] - public ProviderConfigModelProvider? ModelProvider { get; set; } - - /// Unique provider name used to qualify model selection IDs. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - /// Transport used to communicate with the provider. - [JsonPropertyName("transport")] - public ProviderConfigTransport? Transport { get; set; } - - /// Provider protocol family. - [JsonPropertyName("type")] - public ProviderConfigType? Type { get; set; } + /// Existing MC session ID to reattach to. + [JsonPropertyName("mcSessionId")] + public string McSessionId { get; set; } = string.Empty; - /// Wire API used to communicate with the provider. - [JsonPropertyName("wireApi")] - public ProviderConfigWireApi? WireApi { get; set; } + /// Existing MC task ID for the reattached session. + [JsonPropertyName("mcTaskId")] + public string McTaskId { get; set; } = string.Empty; } -/// Provider configuration prepared from a discovered model. Preparing a plan changes nothing: it neither registers the model with the session nor writes durable configuration. To apply it, pass `provider` and `model` to `session.provider.add`, omitting whichever the dispositions report as already configured. +/// Configuration for the runtime-managed remote-control singleton. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderConfigurationPlan +public sealed class RemoteControlConfig { - /// Model definition prepared from the discovered model. Capability fields the provider did not report stay omitted rather than being asserted false. - [JsonPropertyName("model")] - public ProviderModelConfig Model { get => field ??= new(); set; } + /// Reattach to an existing MC session without creating a new one. + [JsonPropertyName("existingMcSession")] + public RemoteControlConfigExistingMcSession? ExistingMcSession { get; set; } - /// Whether `model` still needs to be registered. When `alreadyConfigured`, `selectionId` is already registered and the caller can select it without adding anything. - [JsonPropertyName("modelDisposition")] - public ModelProviderConfigurationDisposition ModelDisposition { get; set; } + /// Whether the user explicitly requested remote (vs. implicit session-sync). Controls warning surfacing for missing-repo cases. + [JsonPropertyName("explicit")] + public bool Explicit { get; set; } - /// Provider connection prepared from the instance's inference metadata. Carries no credential; supply one if the endpoint requires it. - [JsonPropertyName("provider")] - public NamedProviderConfig Provider { get => field ??= new(); set; } + /// Whether remote export should be enabled. + [JsonPropertyName("remote")] + public bool Remote { get; set; } - /// Whether `provider` still needs to be registered. When `alreadyConfigured`, a provider with the same endpoint is already registered and `provider` restates it under its existing name; adding it again is rejected as a duplicate. - [JsonPropertyName("providerDisposition")] - public ModelProviderConfigurationDisposition ProviderDisposition { get; set; } + /// When true, suppresses timeline messages on successful setup. + [JsonPropertyName("silent")] + public bool Silent { get; set; } - /// Provider-qualified selection id (`provider/id`) to pass to `switchTo` once the plan is applied. - [JsonPropertyName("selectionId")] - public string SelectionId { get; set; } = string.Empty; + /// Whether the MC session may steer the local session (write mode). + [JsonPropertyName("steerable")] + public bool Steerable { get; set; } - /// Non-fatal warnings carried over from the discovered model, such as capabilities the provider did not report. - [JsonPropertyName("warnings")] - public IList Warnings { get => field ??= []; set; } + /// Existing Mission Control task ID to attach the exported session to. + [JsonPropertyName("taskId")] + public string? TaskId { get; set; } } -/// A discovered instance and one of its models to translate into provider configuration. Pass back the instance and model as returned by `session.providers.discover` and `session.providers.models.list`. +/// Parameters for attaching the remote-control singleton to a session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelProviderPrepareConfigurationRequest +internal sealed class SessionsStartRemoteControlRequest { - /// The discovered instance that serves the model. - [JsonPropertyName("instance")] - public ModelProviderInstance Instance { get => field ??= new(); set; } - - /// The discovered model to configure. - [JsonPropertyName("model")] - public DiscoveredModel Model { get => field ??= new(); set; } + /// Configuration for the runtime-managed remote-control singleton. + [JsonPropertyName("config")] + public RemoteControlConfig Config { get => field ??= new(); set; } - /// Target session identifier. + /// Local session id to attach remote control to. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Managed sandbox enforcement state for a session. +/// Outcome of a transferRemoteControl call. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxEnforcementStatus +public sealed class RemoteControlTransferResult { - /// Whether an enforcement failure has permanently blocked the session. - [JsonPropertyName("blocked")] - public bool Blocked { get; set; } - - /// The first sandbox enforcement failure that blocked the session. - [JsonPropertyName("reason")] - public string? Reason { get; set; } + /// State of the runtime-managed remote-control singleton. + [JsonPropertyName("status")] + public RemoteControlStatus Status { get => field ??= new(); set; } - /// Whether the effective managed policy requires an available sandbox backend. - [JsonPropertyName("required")] - public bool Required { get; set; } + /// Whether the rebinding actually happened. + [JsonPropertyName("transferred")] + public bool Transferred { get; set; } } -/// Identifies the target session. +/// Parameters for atomically rebinding the remote-control singleton. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSandboxGetEnforcementStatusRequest +internal sealed class SessionsTransferRemoteControlRequest { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// When provided, the transfer is rejected unless the singleton currently points at this session id (compare-and-swap semantics to avoid clobbering newer state). + [JsonPropertyName("expectedFromSessionId")] + public string? ExpectedFromSessionId { get; set; } + + /// Local session id to point remote control at. + [JsonPropertyName("toSessionId")] + public string ToSessionId { get; set; } = string.Empty; } -/// Result of attempting to disable sandboxing for the current session. +/// Patch for the singleton's steering state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxDisableForSessionResult +internal sealed class SessionsSetRemoteControlSteeringRequest { - /// The authoritative sandbox enabled state after the operation. + /// Target steering state. Today only `true` is actionable on the underlying exporter; `false` is reserved for future use. [JsonPropertyName("enabled")] public bool Enabled { get; set; } - - /// Whether this call resolved the pending request and applied the session opt-out. - [JsonPropertyName("success")] - public bool Success { get; set; } } -/// Optional informational context describing how and where the permission decision was made. This does not affect permission behavior. +/// Outcome of a stopRemoteControl call. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionDecisionContext +public sealed class RemoteControlStopResult { - /// Disposition of the permission request as observed by the responding client. - [JsonPropertyName("outcome")] - public PermissionDecisionOutcome Outcome { get; set; } - - /// Whether the responding client could ask a user interactively, was running headlessly, or had no response path. Omit when the client cannot determine this authoritatively. - [JsonPropertyName("responseCapability")] - public PermissionResponseCapability? ResponseCapability { get; set; } - - /// Controlled reason or actor responsible for the response. - [JsonPropertyName("source")] - public PermissionDecisionSource Source { get; set; } + /// State of the runtime-managed remote-control singleton. + [JsonPropertyName("status")] + public RemoteControlStatus Status { get => field ??= new(); set; } - /// Client surface that submitted the response. - [JsonPropertyName("surface")] - public PermissionDecisionSurface Surface { get; set; } + /// Whether the singleton was actually torn down by this call. + [JsonPropertyName("stopped")] + public bool Stopped { get; set; } } -/// Request to disable sandboxing for the current session while resolving an active sandbox-bypass permission prompt. +/// RPC data type for SessionsStopRemoteControl operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SandboxDisableForSessionRequest +internal sealed class SessionsStopRemoteControlRequest { - /// Optional attribution for the permission decision. - [JsonPropertyName("decisionContext")] - public PermissionDecisionContext? DecisionContext { get; set; } + /// When provided, the stop is rejected unless the singleton currently points at this session id (compare-and-swap semantics). + [JsonPropertyName("expectedSessionId")] + public string? ExpectedSessionId { get; set; } - /// Identifier of the exact pending sandbox-bypass permission request that authorized the session opt-out. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// When true, the singleton is unconditionally torn down regardless of `expectedSessionId`. Use during shutdown or explicit `/remote off`. + [JsonPropertyName("force")] + public bool? Force { get; set; } +} - /// Target session identifier. +/// Params to attach or detach an in-process ExtensionController delegate. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ConfigureSessionExtensionsParams +{ + /// Session to attach the extension controller delegate to. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of accepting a sandbox path grant. +/// Outcome of an agentRegistry.spawn call. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxGrantPathForRequestResult +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(AgentRegistrySpawnResultSpawned), "spawned")] +[JsonDerivedType(typeof(AgentRegistrySpawnResultSpawnError), "spawn-error")] +[JsonDerivedType(typeof(AgentRegistrySpawnResultRegistryTimeout), "registry-timeout")] +[JsonDerivedType(typeof(AgentRegistrySpawnResultValidationError), "validation-error")] +public partial class AgentRegistrySpawnResult { - /// Whether this call resolved the pending request and added the path to the session's sandbox policy. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// Request to accept the sandbox path grant offered on an active sandbox escalation permission prompt. + +/// Full registry entry for the spawned child. Lets the controller call `handleLiveTargetSelected(entry)` directly without re-reading the registry (avoids a TOCTOU window). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SandboxGrantPathForRequestRequest +public sealed class AgentRegistryLiveTargetEntry { - /// Optional attribution for the permission decision. - [JsonPropertyName("decisionContext")] - public PermissionDecisionContext? DecisionContext { get; set; } + /// Kind of attention required when status === "attention". Meaningful only when status === "attention". + [JsonPropertyName("attentionKind")] + public AgentRegistryLiveTargetEntryAttentionKind? AttentionKind { get; set; } - /// Identifier of the exact pending sandbox escalation permission request whose sandboxPathGrant to accept. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Git branch of the session (when known). + [JsonPropertyName("branch")] + public string? Branch { get; set; } -/// Authentication status and account metadata for the session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionAuthStatus -{ - /// Authentication type. - [JsonPropertyName("authType")] - public AuthInfoType? AuthType { get; set; } + /// Copilot CLI version that wrote the entry. + [JsonPropertyName("copilotVersion")] + public string CopilotVersion { get; set; } = string.Empty; - /// Copilot plan tier (e.g., individual_pro, business). - [JsonPropertyName("copilotPlan")] - public string? CopilotPlan { get; set; } + /// Working directory of the session (when known). + [JsonPropertyName("cwd")] + public string? Cwd { get; set; } - /// Authentication host URL. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] + /// Bind host for the entry's JSON-RPC server. [JsonPropertyName("host")] - public string? Host { get; set; } - - /// Whether the session has resolved authentication. - [JsonPropertyName("isAuthenticated")] - public bool IsAuthenticated { get; set; } - - /// Authenticated login/username, if available. - [JsonPropertyName("login")] - public string? Login { get; set; } - - /// Human-readable authentication status description. - [JsonPropertyName("statusMessage")] - public string? StatusMessage { get; set; } -} - -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionGitHubAuthGetStatusRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} - -/// Indicates whether the credential update succeeded. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionSetCredentialsResult -{ - /// Whether the session ended up with a populated `copilotUser` for the installed credentials. `true` when the supplied credential already carried `copilotUser` or it was successfully re-resolved server-side. `false` when the credential is installed without `copilotUser` — either re-resolution failed, or the variant cannot be re-resolved from the credential alone (only the raw-token variants `token`, `env`, and `gh-cli` can). In both `false` cases the token swap still applied, but plan/quota/billing metadata is degraded. Present whenever a credential was supplied; omitted only when no credential was supplied (no-op call). - [JsonPropertyName("copilotUserResolved")] - public bool? CopilotUserResolved { get; set; } + public string Host { get; set; } = string.Empty; - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } -} + /// Process kind tag for the registry entry. + [JsonPropertyName("kind")] + public AgentRegistryLiveTargetEntryKind Kind { get; set; } -/// Authentication credentials accepted by session.gitHubAuth.setCredentials. Session-owned token-provider identities cannot be installed through this method. -/// Polymorphic base type discriminated by type. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "type", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(SettableAuthInfoHmac), "hmac")] -[JsonDerivedType(typeof(SettableAuthInfoEnv), "env")] -[JsonDerivedType(typeof(SettableAuthInfoToken), "token")] -[JsonDerivedType(typeof(SettableAuthInfoCopilotApiToken), "copilot-api-token")] -[JsonDerivedType(typeof(SettableAuthInfoUser), "user")] -[JsonDerivedType(typeof(SettableAuthInfoAccount), "account")] -[JsonDerivedType(typeof(SettableAuthInfoGhCli), "gh-cli")] -[JsonDerivedType(typeof(SettableAuthInfoApiKey), "api-key")] -public partial class SettableAuthInfo -{ - /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { get; set; } = string.Empty; -} + /// Wall-clock milliseconds since the watcher last observed this entry (heartbeat freshness). + [JsonPropertyName("lastSeenMs")] + public long LastSeenMs { get; set; } + /// How the most recent turn ended (clean vs aborted). Lets the renderer distinguish done from done_cancelled. + [JsonPropertyName("lastTerminalEvent")] + public AgentRegistryLiveTargetEntryLastTerminalEvent? LastTerminalEvent { get; set; } -/// Authentication-info input variant for GitHub-internal HMAC auth, carrying the public GitHub host and HMAC secret. -/// The hmac variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SettableAuthInfoHmac : SettableAuthInfo -{ - /// - [JsonIgnore] - public override string Type => "hmac"; + /// Model identifier currently selected for the session. + [JsonPropertyName("model")] + public string? Model { get; set; } - /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("copilotUser")] - public CopilotUserResponse? CopilotUser { get; set; } + /// Operating-system pid of the process owning this entry. + [JsonPropertyName("pid")] + public long Pid { get; set; } - /// HMAC secret used to sign requests. - [JsonPropertyName("hmac")] - public required string Hmac { get; set; } + /// TCP port the entry's JSON-RPC server is listening on. + [JsonPropertyName("port")] + public long Port { get; set; } - /// Authentication host. HMAC auth always targets the public GitHub host. - [JsonPropertyName("host")] - public required string Host { get; set; } -} + /// Registry entry schema version (1 = ui-server, 2 = managed-server). + [JsonPropertyName("schemaVersion")] + public long SchemaVersion { get; set; } -/// Authentication-info input variant for a token sourced from an environment variable, with host, optional login, token, and env var name. -/// The env variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SettableAuthInfoEnv : SettableAuthInfo -{ - /// - [JsonIgnore] - public override string Type => "env"; + /// Session ID of the foreground session for this entry. + [JsonPropertyName("sessionId")] + public string? SessionId { get; set; } - /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("copilotUser")] - public CopilotUserResponse? CopilotUser { get; set; } + /// Friendly session name (when set). + [JsonPropertyName("sessionName")] + public string? SessionName { get; set; } - /// Name of the environment variable the token was sourced from. - [JsonPropertyName("envVar")] - public required string EnvVar { get; set; } + /// ISO 8601 timestamp captured at registration. + [JsonPropertyName("startedAt")] + public string StartedAt { get; set; } = string.Empty; - /// Authentication host (e.g. https://github.com or a GHES host). - [JsonPropertyName("host")] - public required string Host { get; set; } + /// Coarse lifecycle status of the foreground session. + [JsonPropertyName("status")] + public AgentRegistryLiveTargetEntryStatus? Status { get; set; } - /// User login associated with the token. Undefined for server-to-server tokens (those starting with `ghs_`). - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("login")] - public string? Login { get; set; } + /// Monotonic per-publisher revision counter incremented on every status update. Lets watchers detect transient flips. + [JsonPropertyName("statusRevision")] + public long? StatusRevision { get; set; } - /// The token value itself. Treat as a secret. + /// Connection token (null when the target is unauthenticated). + [JsonInclude] [JsonPropertyName("token")] - public required string Token { get; set; } + internal string? Token { get; set; } } -/// Token authentication accepted by session.gitHubAuth.setCredentials. -/// The token variant of . +/// Canonical process-log discovery outcome; populated from spawnLiveTarget. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SettableAuthInfoToken : SettableAuthInfo +public sealed class AgentRegistryLogCapture { - /// - [JsonIgnore] - public override string Type => "token"; + /// Whether a canonical process log was discovered for this managed spawn. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } - /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("copilotUser")] - public CopilotUserResponse? CopilotUser { get; set; } + /// Why no canonical process log could be opened for this managed spawn (set only when enabled is false). + [JsonPropertyName("openError")] + public string? OpenError { get; set; } - /// Authentication host. - [JsonPropertyName("host")] - public required string Host { get; set; } + /// Categorized reason no canonical process log could be opened (set only when enabled is false). + [JsonPropertyName("openErrorReason")] + public AgentRegistryLogCaptureOpenErrorReason? OpenErrorReason { get; set; } - /// The token value itself. Treat as a secret. - [JsonPropertyName("token")] - public required string Token { get; set; } + /// Absolute path to the managed spawn's process-<timestamp>-<pid>.log file (only set when enabled). + [JsonPropertyName("path")] + public string? Path { get; set; } } -/// Authentication-info variant for direct Copilot API token auth sourced from environment variables, with public GitHub host. -/// The copilot-api-token variant of . +/// Managed-server child was spawned and registered successfully. +/// The spawned variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SettableAuthInfoCopilotApiToken : SettableAuthInfo +public partial class AgentRegistrySpawnResultSpawned : AgentRegistrySpawnResult { /// [JsonIgnore] - public override string Type => "copilot-api-token"; + public override string Kind => "spawned"; - /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. + /// Full registry entry for the spawned child. Lets the controller call `handleLiveTargetSelected(entry)` directly without re-reading the registry (avoids a TOCTOU window). + [JsonPropertyName("entry")] + public required AgentRegistryLiveTargetEntry Entry { get; set; } + + /// If the delegate attempted to send the initial prompt and failed, the categorized error message. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("copilotUser")] - public CopilotUserResponse? CopilotUser { get; set; } + [JsonPropertyName("initialPromptError")] + public string? InitialPromptError { get; set; } - /// Authentication host (always the public GitHub host). - [JsonPropertyName("host")] - public required string Host { get; set; } + /// Whether the delegate already sent the initial prompt. Always omitted in the current wiring: the controller sends the prompt post-attach via the standard LocalRpcSession.send path. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("initialPromptSent")] + public bool? InitialPromptSent { get; set; } + + /// Per-spawn log-capture outcome; populated from spawnLiveTarget. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("logCapture")] + public AgentRegistryLogCapture? LogCapture { get; set; } } -/// Authentication-info variant for OAuth user auth, with host and login; the token remains in the runtime secret store. -/// The user variant of . +/// `child_process.spawn` itself failed before the child entered the registry. +/// The spawn-error variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SettableAuthInfoUser : SettableAuthInfo +public partial class AgentRegistrySpawnResultSpawnError : AgentRegistrySpawnResult { /// [JsonIgnore] - public override string Type => "user"; + public override string Kind => "spawn-error"; - /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. + /// Underlying errno code (e.g. ENOENT, EACCES) when available. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("copilotUser")] - public CopilotUserResponse? CopilotUser { get; set; } - - /// Authentication host. - [JsonPropertyName("host")] - public required string Host { get; set; } + [JsonPropertyName("code")] + public string? Code { get; set; } - /// OAuth user login. - [JsonPropertyName("login")] - public required string Login { get; set; } + /// Human-readable error message. + [JsonPropertyName("message")] + public required string Message { get; set; } } -/// An interactive account whose model provider owns its credentials. It carries no GitHub credential. -/// The account variant of . +/// Spawn succeeded but the child did not publish a matching managed-server entry within the timeout. +/// The registry-timeout variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SettableAuthInfoAccount : SettableAuthInfo +public partial class AgentRegistrySpawnResultRegistryTimeout : AgentRegistrySpawnResult { /// [JsonIgnore] - public override string Type => "account"; + public override string Kind => "registry-timeout"; - /// Host coordinate owned by the account's model provider. - [JsonPropertyName("host")] - public required string Host { get; set; } + /// Process ID of the orphaned child (so the caller can offer 'kill the pid' guidance). + [JsonPropertyName("childPid")] + public required long ChildPid { get; set; } - /// Login identifying the provider-owned account. - [JsonPropertyName("login")] - public required string Login { get; set; } + /// Per-spawn log-capture outcome; populated from spawnLiveTarget. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("logCapture")] + public AgentRegistryLogCapture? LogCapture { get; set; } } -/// Authentication-info input variant for GitHub CLI credentials, carrying host, login, and the `gh auth token` value. -/// The gh-cli variant of . +/// Synchronous pre-validation rejected the spawn request. +/// The validation-error variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SettableAuthInfoGhCli : SettableAuthInfo +public partial class AgentRegistrySpawnResultValidationError : AgentRegistrySpawnResult { /// [JsonIgnore] - public override string Type => "gh-cli"; + public override string Kind => "validation-error"; - /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. + /// Which parameter field was invalid. Omitted when the rejection is not field-specific. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("copilotUser")] - public CopilotUserResponse? CopilotUser { get; set; } - - /// Authentication host. - [JsonPropertyName("host")] - public required string Host { get; set; } + [JsonPropertyName("field")] + public AgentRegistrySpawnValidationErrorField? Field { get; set; } - /// User login as reported by `gh auth status`. - [JsonPropertyName("login")] - public required string Login { get; set; } + /// Human-readable explanation; safe to surface in the UI banner. Never logged to unrestricted telemetry. + [JsonPropertyName("message")] + public required string Message { get; set; } - /// The token returned by `gh auth token`. Treat as a secret. - [JsonPropertyName("token")] - public required string Token { get; set; } + /// Categorized reason for the rejection. Low-cardinality enum so telemetry can aggregate by reason without leaking raw paths or agent/model names. + [JsonPropertyName("reason")] + public required AgentRegistrySpawnValidationErrorReason Reason { get; set; } } -/// Authentication-info input variant for API-key authentication to a non-GitHub LLM provider, carrying the secret `apiKey` and host. -/// The api-key variant of . +/// Inputs to spawn a managed-server child via the controller's spawn delegate. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SettableAuthInfoApiKey : SettableAuthInfo +internal sealed class AgentRegistrySpawnRequest { - /// - [JsonIgnore] - public override string Type => "api-key"; + /// Custom or built-in agent name (e.g. 'explore'). When omitted, the child uses its own default. + [JsonPropertyName("agentName")] + public string? AgentName { get; set; } - /// The API key. Treat as a secret. - [JsonPropertyName("apiKey")] - public required string ApiKey { get; set; } + /// Working directory for the spawned child (must be an existing directory). + [JsonPropertyName("cwd")] + public string Cwd { get; set; } = string.Empty; - /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("copilotUser")] - public CopilotUserResponse? CopilotUser { get; set; } + /// Optional first user message. Forwarded to the caller (the CLI's spawn wrapper sends it post-attach via the standard LocalRpcSession.send path). + [JsonPropertyName("initialPrompt")] + public string? InitialPrompt { get; set; } - /// Authentication host. - [JsonPropertyName("host")] - public required string Host { get; set; } + /// Model identifier to apply to the new session. + [JsonPropertyName("model")] + public string? Model { get; set; } + + /// Friendly session name. Must satisfy validateSessionName: non-empty, no leading/trailing whitespace, <=100 chars, no control chars, no double quotes. + [JsonPropertyName("name")] + public string? Name { get; set; } + + /// Permission posture for the new session. 'yolo' requires the controller-local session to currently be in allow-all mode. + [JsonPropertyName("permissionMode")] + public AgentRegistrySpawnPermissionMode? PermissionMode { get; set; } } -/// New auth credentials to install on the session. Omit to leave credentials unchanged. +/// Feature availability. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSetCredentialsParams +public sealed class ConnectorDiscoveryCapabilities { - /// The new auth credentials to install on the session. When omitted or `undefined`, the call is a no-op and the session's existing credentials are preserved. The runtime installs the supplied value immediately for outbound model/API requests. When the credential carries a raw token (`token`, `env`, or `gh-cli`) but no `copilotUser`, the runtime additionally re-resolves `copilotUser` server-side (best-effort, asynchronously, after the synchronous install) so plan/quota/billing metadata regains fidelity; on resolution failure the verbatim credential remains installed. It does NOT otherwise validate the credential. Several variants carry secret material; treat this method's params as containing secrets at rest and in transit. - [JsonPropertyName("credentials")] - public SettableAuthInfo? Credentials { get; set; } + /// API version. + [JsonPropertyName("apiVersion")] + public long ApiVersion { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Availability. + [JsonPropertyName("availability")] + public ConnectorDiscoveryAvailability Availability { get; set; } + + /// Whether results are cached. + [JsonPropertyName("conditionalCache")] + public bool ConditionalCache { get; set; } + + /// Whether accounts are selected by opaque ID. + [JsonPropertyName("opaqueAccountSelection")] + public bool OpaqueAccountSelection { get; set; } } -/// Credential-free authentication identity safe to expose to hosts and user interfaces. +/// Account metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AuthIdentity +public sealed class ConnectorDiscoveryAuthInfo { - /// Snapshot of the authenticated user's Copilot subscription info, if known. - [JsonPropertyName("copilotUser")] - public CopilotUserResponse? CopilotUser { get; set; } - - /// Name of the environment variable that supplied the credential, when applicable. - [JsonPropertyName("envVar")] - public string? EnvVar { get; set; } - - /// Authentication host. + /// Host. [JsonPropertyName("host")] public string Host { get; set; } = string.Empty; - /// Authenticated login, when available. + /// Login. [JsonPropertyName("login")] - public string? Login { get; set; } - - /// Opaque SDK GitHub credential registration backing this identity. Routing metadata only; never a credential. - [JsonPropertyName("registrationId")] - public string? RegistrationId { get; set; } + public string Login { get; set; } = string.Empty; /// Authentication type. [JsonPropertyName("type")] public AuthInfoType Type { get; set; } } -/// Identifies the target session. +/// Eligible account. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionGitHubAuthGetCurrentAuthInfoRequest +public sealed class ConnectorDiscoveryAccount { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Opaque account ID. + [JsonPropertyName("accountId")] + public string AccountId { get; set; } = string.Empty; -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionGitHubAuthGetAllAuthAvailableRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Account metadata. + [JsonPropertyName("authInfo")] + public ConnectorDiscoveryAuthInfo AuthInfo { get => field ??= new(); set; } } -/// Identifies the target session. +/// Eligible accounts. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionGitHubAuthRefreshCopilotUserRequest +public sealed class ConnectorDiscoveryAccountList { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Eligible accounts. + [JsonPropertyName("accounts")] + public IList Accounts { get => field ??= []; set; } + + /// Availability. + [JsonPropertyName("availability")] + public ConnectorDiscoveryAvailability Availability { get; set; } } -/// Internal GitHub login parameters. +/// Entry. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionAuthLoginRequest +public sealed class ConnectorDiscoveryCatalogEntry { - /// GitHub host URL. - [JsonPropertyName("host")] - public string Host { get; set; } = string.Empty; + /// Description. + [JsonPropertyName("description")] + public string? Description { get; set; } - /// GitHub login. - [JsonPropertyName("login")] - public string Login { get; set; } = string.Empty; + /// Display name. + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } = string.Empty; - /// Whether to persist the token after login. - [JsonPropertyName("persist")] - public bool? Persist { get; set; } + /// Logo. + [JsonPropertyName("logo")] + public string? Logo { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// GitHub authentication token. - [JsonPropertyName("token")] - public string Token { get; set; } = string.Empty; + /// Release tag. + [JsonPropertyName("releaseTag")] + public string? ReleaseTag { get; set; } + + /// Status. + [JsonPropertyName("status")] + public ConnectorCatalogStatus Status { get; set; } + + /// Tier. + [JsonPropertyName("tier")] + public string? Tier { get; set; } } -/// Parameters for switching the session's active authentication. +/// Entries for the selected account. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionAuthSwitchRequest +public sealed class ConnectorDiscoveryCatalogResult { - /// Authentication information to activate. - [JsonPropertyName("authInfo")] - public AuthInfo AuthInfo { get => field ??= new(); set; } + /// Opaque account ID. + [JsonPropertyName("accountId")] + public string AccountId { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Entries. + [JsonPropertyName("connectors")] + public IList Connectors { get => field ??= []; set; } - /// Optional token paired with the authentication information. - [JsonPropertyName("token")] - public string? Token { get; set; } + /// Refresh time in Unix epoch milliseconds. + [JsonPropertyName("refreshedAtMs")] + public long RefreshedAtMs { get; set; } + + /// Revision. + [JsonPropertyName("revision")] + public long Revision { get; set; } +} + +/// Selected account. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ConnectorDiscoveryAccountRequest +{ + /// Opaque account ID. + [RegularExpression("^\\S+$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(2048)] + [JsonPropertyName("accountId")] + public string AccountId { get; set; } = string.Empty; } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionGitHubAuthLogoutRequest +internal sealed class SessionSuspendRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Parameters identifying a GitHub authentication to log out. +/// Result of sending a user message. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionAuthLogoutUserRequest +public sealed class SendResult { - /// Authentication information to log out. - [JsonPropertyName("authInfo")] - public AuthInfo AuthInfo { get => field ??= new(); set; } - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} - -/// Validation error from an authentication attempt. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AuthValidationError -{ - /// Optional message returned by GitHub. - [JsonPropertyName("githubMessage")] - public string? GitHubMessage { get; set; } - - /// Authentication validation error message. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; -} - -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionGitHubAuthLastAuthErrorsRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Unique identifier assigned to the message. + [JsonPropertyName("messageId")] + public string MessageId { get; set; } = string.Empty; } -/// The enumerated collection, keyed by the same selector as the query. -/// Polymorphic base type discriminated by kind. +/// Provider-native structured output format. JSON Schema is forwarded without rewriting or validating the schema or the generated output. +/// Polymorphic base type discriminated by type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", + TypeDiscriminatorPropertyName = "type", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(AuthEnumerateValueAccounts), "accounts")] -[JsonDerivedType(typeof(AuthEnumerateValueProviders), "providers")] -public partial class AuthEnumerateValue +[JsonDerivedType(typeof(ResponseFormatJsonSchema), "json_schema")] +public partial class ResponseFormat { /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; + [JsonPropertyName("type")] + public virtual string Type { get; set; } = string.Empty; } -/// One signed-in account in the roster forest. +/// A JSON Schema output contract. OpenAI receives the name, description, schema and strict setting; Anthropic receives the schema in output_config.format and always uses its native strict enforcement. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AccountStatus +public sealed class JsonSchemaResponseFormat { - /// Whether this is the active account. - [JsonPropertyName("active")] - public bool Active { get; set; } - - /// Opaque id of the account this one was derived from (e.g. an EMU account's base Entra identity); absent for a root account. Matches the base identity account's selectionId, forming the derivation edge. - [JsonPropertyName("derivedFrom")] - public string? DerivedFrom { get; set; } - - /// Authentication host URL. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("host")] - public string Host { get; set; } = string.Empty; + /// Optional description passed to OpenAI providers. + [JsonPropertyName("description")] + public string? Description { get; set; } - /// The provider kind of this account. - [JsonPropertyName("kind")] - public AccountKind Kind { get; set; } + /// Name of the output schema, subject to the provider's naming restrictions. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Authenticated login/username. - [JsonPropertyName("login")] - public string Login { get; set; } = string.Empty; + /// JSON Schema passed unchanged to the inference provider. Schemas larger than 32 MiB when JSON-encoded are rejected before admission, using the runtime's existing request-size ceiling. This is not a guarantee that the entire model request fits. Supported keywords and schema restrictions are determined by the provider. + [JsonPropertyName("schema")] + public JsonElement Schema { get; set; } - /// Opaque selection id used to switch to, or log out, this account. - [JsonPropertyName("selectionId")] - public string SelectionId { get; set; } = string.Empty; + /// Optional strict enforcement setting for OpenAI providers. Omitted uses the provider default. Anthropic always enforces its supported schema subset. + [JsonPropertyName("strict")] + public bool? Strict { get; set; } } -/// The accounts variant of . +/// The json_schema variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthEnumerateValueAccounts : AuthEnumerateValue +public partial class ResponseFormatJsonSchema : ResponseFormat { /// [JsonIgnore] - public override string Kind => "accounts"; + public override string Type => "json_schema"; - /// The signed-in account forest; empty when not logged in. - [JsonPropertyName("items")] - public required IList Items { get; set; } + /// JSON Schema and provider options for the turn's output. + [JsonPropertyName("jsonSchema")] + public required JsonSchemaResponseFormat JsonSchema { get; set; } } -/// A provider offered for interactive login. +/// Parameters for sending a user message to the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderDescriptor +internal sealed class SendRequest { - /// Whether this provider is currently available to sign in with. - [JsonPropertyName("available")] - public bool Available { get; set; } - - /// The neutral provider kind. - [JsonPropertyName("kind")] - public LoginProviderKind Kind { get; set; } + /// The UI mode the agent was in when this message was sent. Defaults to the session's current mode. + [JsonPropertyName("agentMode")] + public SendAgentMode? AgentMode { get; set; } - /// Human-readable menu label, owned by the runtime so every consumer renders identical text. - [JsonPropertyName("label")] - public string Label { get; set; } = string.Empty; -} + /// Optional attachments (files, directories, selections, blobs, GitHub references) to include with the message. + [JsonPropertyName("attachments")] + public IList? Attachments { get; set; } -/// The providers variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthEnumerateValueProviders : AuthEnumerateValue -{ - /// - [JsonIgnore] - public override string Kind => "providers"; + /// If false, this message will not trigger a Premium Request Unit charge. User messages default to billable. + [JsonPropertyName("billable")] + public bool? Billable { get; set; } - /// The providers offered for interactive login. - [JsonPropertyName("items")] - public required IList Items { get; set; } -} + /// If provided, this is shown in the timeline instead of `prompt`. + [JsonPropertyName("displayPrompt")] + public string? DisplayPrompt { get; set; } -/// Selects which accounts collection to enumerate. A no-arg selector is the empty-payload variant. -/// Polymorphic base type discriminated by kind. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(AuthEnumerateQueryAccounts), "accounts")] -[JsonDerivedType(typeof(AuthEnumerateQueryProviders), "providers")] -public partial class AuthEnumerateQuery -{ - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// How to deliver the message. `enqueue` (default) appends to the message queue. `immediate` interjects during an in-progress turn. + [JsonPropertyName("mode")] + public SendMode? Mode { get; set; } + /// If true, adds the message to the front of the queue instead of the end. + [JsonPropertyName("prepend")] + public bool? Prepend { get; set; } -/// The accounts variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthEnumerateQueryAccounts : AuthEnumerateQuery -{ - /// - [JsonIgnore] - public override string Kind => "accounts"; -} + /// The user message text. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; -/// The providers variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthEnumerateQueryProviders : AuthEnumerateQuery -{ - /// - [JsonIgnore] - public override string Kind => "providers"; + /// Custom HTTP headers to include in outbound model requests for this turn. Merged with session-level provider headers; per-turn headers augment and overwrite session-level headers with the same key. + [JsonPropertyName("requestHeaders")] + public IDictionary? RequestHeaders { get; set; } - /// Whether an interactive Entra broker is available on the host; gates Entra availability in the returned list. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("brokerAvailable")] - public bool? BrokerAvailable { get; set; } -} + /// If set, the request will fail if the named tool is not available when this message is among the user messages at the start of the current exchange. + [JsonPropertyName("requiredTool")] + public string? RequiredTool { get; set; } -/// Enumerate request carrying the typed collection query. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AccountsEnumerateRequest -{ - /// Which typed accounts collection to enumerate. - [JsonPropertyName("query")] - public AuthEnumerateQuery Query { get => field ??= new(); set; } + /// Provider-native output format for this turn, including all tool-call iterations. Not inherited by later turns or subagents. Ordinary steering inherits the active format; specifying responseFormat with mode: immediate is an error, even while idle. Returned assistant content remains text; the runtime does not parse or validate it. Unsupported models or schemas produce provider errors. + [JsonPropertyName("responseFormat")] + public ResponseFormat? ResponseFormat { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Optional provenance tag copied to the resulting user.message event. Must be `user`, `system`, `command-<command-id>` for command-originated messages, `schedule-<numeric-id>` for scheduled prompts, or `agent-<agent-id>` for prompts sent by another agent. + [RegularExpression("^(user|system|command-.*|schedule-\\d+|agent-.+)$")] + [JsonInclude] + [JsonPropertyName("source")] + internal string? Source { get; set; } + + /// W3C Trace Context traceparent header for distributed tracing of this agent turn. + [JsonPropertyName("traceparent")] + public string? Traceparent { get; set; } + + /// W3C Trace Context tracestate header for distributed tracing. + [JsonPropertyName("tracestate")] + public string? Tracestate { get; set; } + + /// If true, await completion of the agentic loop for this message before returning. Defaults to false (fire-and-forget). When true, the result still contains the same `messageId`; the caller can rely on the agent having processed the message before the call resolves. Transport-dependent tail semantics: on a LOCAL (in-process) session the wait additionally blocks until the completed turn's event tail has been dispatched to this session's in-process subscribers, so a subsequent read of subscriber state already reflects the turn; on a REMOTE session the wait resolves once the loop completes and mirrored delivery follows over the wire. Callers that need the stronger local guarantee on remote sessions should await the event stream explicitly. + [JsonPropertyName("wait")] + public bool? Wait { get; set; } } -/// The read result, keyed by the same selector as the query. -/// Polymorphic base type discriminated by kind. +/// Result of sending zero or more user messages. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(AuthReadValueActiveAccount), "activeAccount")] -[JsonDerivedType(typeof(AuthReadValueStatus), "status")] -[JsonDerivedType(typeof(AuthReadValueLastErrors), "lastErrors")] -public partial class AuthReadValue +public sealed class SendMessagesResult { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; + /// Unique identifiers assigned to the messages, one per provided message in order. For a batch that starts a run, assistant messages use the final ID as originatingMessageId throughout that run, including tool iterations and stop-hook corrections. Immediate steering does not replace the active run's origin. Empty when no messages were provided; that run has no originatingMessageId. + [JsonPropertyName("messageIds")] + public IList MessageIds { get => field ??= []; set; } } - -/// The activeAccount variant of . +/// A single user message to append to the session as part of a `session.sendMessages` turn. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthReadValueActiveAccount : AuthReadValue +public sealed class SendMessageItem { - /// - [JsonIgnore] - public override string Kind => "activeAccount"; + /// Optional attachments (files, directories, selections, blobs, GitHub references) to include with this message. + [JsonPropertyName("attachments")] + public IList? Attachments { get; set; } - /// The active account, or absent when not logged in. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("account")] - public AccountStatus? Account { get; set; } + /// If false, this message will not trigger a Premium Request Unit charge. User messages default to billable. + [JsonInclude] + [JsonPropertyName("billable")] + internal bool? Billable { get; set; } - /// Credential-free identity metadata for the active account, including resolved Copilot user information when available. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("authInfo")] - public AuthIdentity? AuthInfo { get; set; } + /// If provided, this is shown in the timeline instead of `prompt`. + [JsonPropertyName("displayPrompt")] + public string? DisplayPrompt { get; set; } + + /// The user message text. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; + + /// If set, the request will fail if the named tool is not available when this message is among the user messages at the start of the current exchange. + [JsonPropertyName("requiredTool")] + public string? RequiredTool { get; set; } + + /// Optional provenance tag copied to the resulting user.message event. Must be `user`, `system`, `command-<command-id>` for command-originated messages, `schedule-<numeric-id>` for scheduled prompts, or `agent-<agent-id>` for prompts sent by another agent. + [RegularExpression("^(user|system|command-.*|schedule-\\d+|agent-.+)$")] + [JsonInclude] + [JsonPropertyName("source")] + internal string? Source { get; set; } } -/// Neutral authentication status summary. +/// Parameters for sending zero or more user messages to the session in a single turn. Remote-backed (Mission Control) sessions do not support this method and will return an error. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AuthStatusDto +internal sealed class SendMessagesRequest { - /// Number of signed-in accounts in the roster. - [JsonPropertyName("accountCount")] - public long AccountCount { get; set; } + /// The UI mode the agent was in when these messages were sent. Defaults to the session's current mode. + [JsonPropertyName("agentMode")] + public SendAgentMode? AgentMode { get; set; } - /// Active account host, if authenticated. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("activeHost")] - public string? ActiveHost { get; set; } + /// The user messages to append to the conversation, in order, before running one agent loop. When the batch starts a run, its final message is the primary initiating message; earlier messages provide context, not separate runs or replies. May be empty, in which case a single turn runs over the existing history with no new user message or originatingMessageId. + [JsonPropertyName("messages")] + public IList Messages { get => field ??= []; set; } - /// Active account login, if authenticated. - [JsonPropertyName("activeLogin")] - public string? ActiveLogin { get; set; } + /// How to deliver the messages. `enqueue` (default) appends to the message queue. `immediate` interjects during an in-progress turn. + [JsonPropertyName("mode")] + public SendMode? Mode { get; set; } - /// Copilot plan tier of the active account, if known. - [JsonPropertyName("copilotPlan")] - public string? CopilotPlan { get; set; } + /// If true, adds the messages to the front of the queue instead of the end. + [JsonPropertyName("prepend")] + public bool? Prepend { get; set; } - /// Whether the session has resolved authentication. - [JsonPropertyName("isAuthenticated")] - public bool IsAuthenticated { get; set; } -} + /// Custom HTTP headers to include in outbound model requests for this turn. Merged with session-level provider headers; per-turn headers augment and overwrite session-level headers with the same key. + [JsonPropertyName("requestHeaders")] + public IDictionary? RequestHeaders { get; set; } -/// The status variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthReadValueStatus : AuthReadValue -{ - /// - [JsonIgnore] - public override string Kind => "status"; + /// Provider-native output format for the whole turn, including an empty message batch and all tool-call iterations. Not inherited by later turns or subagents. Ordinary steering inherits the active format; specifying responseFormat with mode: immediate is an error, even while idle. Returned assistant content remains text; the runtime does not parse or validate it. Unsupported models or schemas produce provider errors. + [JsonPropertyName("responseFormat")] + public ResponseFormat? ResponseFormat { get; set; } - /// The neutral authentication status summary. - [JsonPropertyName("status")] - public required AuthStatusDto Status { get; set; } -} + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; -/// The lastErrors variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthReadValueLastErrors : AuthReadValue -{ - /// - [JsonIgnore] - public override string Kind => "lastErrors"; + /// W3C Trace Context traceparent header for distributed tracing of this agent turn. + [JsonPropertyName("traceparent")] + public string? Traceparent { get; set; } - /// Validation errors from the most recent authentication attempt. - [JsonPropertyName("errors")] - public required IList Errors { get; set; } + /// W3C Trace Context tracestate header for distributed tracing. + [JsonPropertyName("tracestate")] + public string? Tracestate { get; set; } + + /// If true, await completion of the agentic loop for this turn before returning. Defaults to false (fire-and-forget). When true, the result still contains the same `messageIds`; the caller can rely on the agent having processed the messages before the call resolves. Transport-dependent tail semantics: on a LOCAL (in-process) session the wait additionally blocks until the completed turn's event tail has been dispatched to this session's in-process subscribers, so a subsequent read of subscriber state already reflects the turn; on a REMOTE session the wait resolves once the loop completes and mirrored delivery follows over the wire. Callers that need the stronger local guarantee on remote sessions should await the event stream explicitly. + [JsonPropertyName("wait")] + public bool? Wait { get; set; } } -/// Selects which typed accounts datum to read. -/// Polymorphic base type discriminated by kind. +/// Internal request for sending a system notification. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(AuthReadQueryActiveAccount), "activeAccount")] -[JsonDerivedType(typeof(AuthReadQueryStatus), "status")] -[JsonDerivedType(typeof(AuthReadQueryLastErrors), "lastErrors")] -public partial class AuthReadQuery +internal sealed class SendSystemNotificationRequest { - /// The type discriminator. + /// Optional structured notification kind. [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + public JsonElement? Kind { get; set; } + + /// Notification text to deliver to the model. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; + /// Internal delivery options, including passive policy. + [JsonPropertyName("options")] + public JsonElement? Options { get; set; } -/// The activeAccount variant of . + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Result of aborting the current turn. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthReadQueryActiveAccount : AuthReadQuery +public sealed class AbortResult { - /// - [JsonIgnore] - public override string Kind => "activeAccount"; + /// Error message if the abort failed. + [JsonPropertyName("error")] + public string? Error { get; set; } + + /// Whether the abort completed successfully. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// The status variant of . +/// Parameters for aborting the current turn. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthReadQueryStatus : AuthReadQuery +internal sealed class AbortRequest { - /// - [JsonIgnore] - public override string Kind => "status"; + /// Finite reason code describing why the current turn was aborted. + [JsonPropertyName("reason")] + public AbortReason? Reason { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// The lastErrors variant of . +/// Result of interrupting the main agent turn. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthReadQueryLastErrors : AuthReadQuery +public sealed class InterruptMainTurnResult { - /// - [JsonIgnore] - public override string Kind => "lastErrors"; + /// Whether an in-flight main agent turn was interrupted. False when the main loop was not processing. + [JsonPropertyName("interrupted")] + public bool Interrupted { get; set; } } -/// Read request carrying the typed datum query. +/// Parameters for interrupting the main agent turn. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AccountsGetRequest +internal sealed class InterruptMainTurnRequest { - /// Which typed accounts datum to read. - [JsonPropertyName("query")] - public AuthReadQuery Query { get => field ??= new(); set; } + /// When true, the user's queued prompts are preserved and run as the next turn once the interrupted turn unwinds; when false (the default), the queue is cleared like a plain abort. + [JsonPropertyName("flushQueued")] + public bool? FlushQueued { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of a non-interactive accounts mutation. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AuthWriteResult +internal sealed class SessionCancelAllBackgroundAgentsRequest { - /// For a logout, whether other signed-in accounts remain. - [JsonPropertyName("moreUsers")] - public bool? MoreUsers { get; set; } - - /// Whether the mutation was applied. - [JsonPropertyName("ok")] - public bool Ok { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// One non-interactive accounts mutation command (the selector is fused with its typed args). -/// Polymorphic base type discriminated by kind. +/// Parameters for shutting down the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(AuthWriteSwitchActive), "switchActive")] -[JsonDerivedType(typeof(AuthWriteLogout), "logout")] -[JsonDerivedType(typeof(AuthWriteSetCredentials), "setCredentials")] -public partial class AuthWrite +internal sealed class ShutdownRequest { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// Dispatch deferred sessionEnd hooks in the background with their full per-hook timeoutSec instead of awaiting them under the short shared shutdown budget. Set this when the host process keeps running after the session closes (for example the CLI's /clear), so a slow hook neither blocks the close nor is aborted. Hooks still detached when the process later exits are terminated with it. Defaults to false. + [JsonPropertyName("detachSessionEndHooks")] + public bool? DetachSessionEndHooks { get; set; } + /// Optional human-readable reason. Typically the message of the error that triggered shutdown when type is 'error'. + [JsonPropertyName("reason")] + public string? Reason { get; set; } -/// The switchActive variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthWriteSwitchActive : AuthWrite -{ - /// - [JsonIgnore] - public override string Kind => "switchActive"; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Opaque selection id of the account to make active. - [JsonPropertyName("selectionId")] - public required string SelectionId { get; set; } + /// Why the session is being shut down. Defaults to "routine" when omitted. + [JsonPropertyName("type")] + public ShutdownType? Type { get; set; } } -/// The logout variant of . +/// Identifier of the session event that was emitted for the log message. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthWriteLogout : AuthWrite +public sealed class LogResult { - /// - [JsonIgnore] - public override string Kind => "logout"; - - /// Opaque selection id of the account to log out; absent logs out the active account. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("selectionId")] - public string? SelectionId { get; set; } + /// The unique identifier of the emitted session event. + [JsonPropertyName("eventId")] + public Guid EventId { get; set; } } -/// The setCredentials variant of . +/// Message text, optional severity level, persistence flag, optional follow-up URL, and optional tip. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthWriteSetCredentials : AuthWrite +internal sealed class LogRequest { - /// - [JsonIgnore] - public override string Kind => "setCredentials"; - - /// Authentication host URL. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("host")] - public required string Host { get; set; } + /// When true, the message is transient and not persisted to the session event log on disk. + [JsonPropertyName("ephemeral")] + public bool? Ephemeral { get; set; } - /// Login/username for the credential. - [JsonPropertyName("login")] - public required string Login { get; set; } + /// Log severity level. Determines how the message is displayed in the timeline. Defaults to "info". + [JsonPropertyName("level")] + public SessionLogLevel? Level { get; set; } - /// GitHub authentication token to install. - [JsonPropertyName("token")] - public required string Token { get; set; } -} - -/// Mutation request carrying the typed write command. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AccountsSetRequest -{ - /// The non-interactive mutation command to apply. - [JsonPropertyName("command")] - public AuthWrite Command { get => field ??= new(); set; } + /// Human-readable message. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Optional actionable tip displayed alongside the message. Only honored on `level: "info"`. + [JsonPropertyName("tip")] + public string? Tip { get; set; } + + /// Domain category for this log entry (e.g., "mcp", "subscription", "policy", "model"). Maps to `infoType`/`warningType`/`errorType` on the emitted event. Defaults to "notification". + [JsonPropertyName("type")] + public string? Type { get; set; } + + /// Optional URL the user can open in their browser for more details. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("url")] + public string? Url { get; set; } } -/// One step in an interactive login flow. The consumer acts on the step and calls advance to proceed. Browser-open is encoded as two distinct steps by design: `open-url` is CONSUMER-driven (the provider surfaces the authorize URL and the consumer opens it — github.com/GHEC web), while `needs-interaction` is PROVIDER-driven (the provider opens the browser or broker UI itself and does not surface a URL — Entra). -/// Polymorphic base type discriminated by kind. +/// Adapter-declared policy that tells clients whether discovery may run automatically. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(AuthLoginStepOpenUrl), "open-url")] -[JsonDerivedType(typeof(AuthLoginStepInputRequired), "input-required")] -[JsonDerivedType(typeof(AuthLoginStepAwaiting), "awaiting")] -[JsonDerivedType(typeof(AuthLoginStepNeedsInteraction), "needs-interaction")] -[JsonDerivedType(typeof(AuthLoginStepCompleted), "completed")] -[JsonDerivedType(typeof(AuthLoginStepError), "error")] -public partial class AuthLoginStep +public sealed class ModelProviderAutomaticDiscoveryPolicy { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; + /// Whether automatic discovery is allowed, limited to configured providers, or explicit-only. + [JsonPropertyName("mode")] + public ModelProviderAutomaticDiscoveryMode Mode { get; set; } + + /// Maximum network scope used by this adapter during discovery. + [JsonPropertyName("networkScope")] + public ModelProviderDiscoveryNetworkScope NetworkScope { get; set; } + + /// True when discovery requires non-null caller input. Omission or null is rejected before adapter execution. When false, omitted or null input selects adapter defaults without schema validation. + [JsonPropertyName("requiresInput")] + public bool RequiresInput { get; set; } + + /// True when the adapter must be enabled by a trusted owner, such as a trusted extension, before automatic discovery may run. + [JsonPropertyName("requiresTrust")] + public bool RequiresTrust { get; set; } } +/// An operation supported by a model-provider adapter. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ModelProviderAdapterOperationDescriptor +{ + /// Optional self-contained JSON Schema Draft 7 for non-null discovery input. Only supported on discover. No external references are resolved. Omitted or null input selects defaults when requiresInput is false. Without a schema, the adapter validates supplied input. + [JsonPropertyName("inputSchema")] + public JsonElement? InputSchema { get; set; } -/// The open-url variant of . + /// Supported operation name: `discover`, `getStatus`, or `models.list`. Unknown names and duplicate declarations are rejected. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; +} + +/// Contributor attribution, independent of routing identity and authorization. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthLoginStepOpenUrl : AuthLoginStep +public sealed class ModelProviderAttribution { - /// - [JsonIgnore] - public override string Kind => "open-url"; + /// Human-readable contributor name, not the adapter display name. + [JsonPropertyName("ownerDisplayName")] + public string? OwnerDisplayName { get; set; } - /// Authorize URL the consumer should open in a browser (consumer-driven browser-open). - [JsonPropertyName("url")] - public required string Url { get; set; } + /// Stable contributor identifier. Required and nonblank for extension and custom sources; optional for built-in and configured sources. Does not grant authority. + [JsonPropertyName("ownerId")] + public string? OwnerId { get; set; } + + /// Kind of component that supplied the adapter. Attribution does not confer authority. + [JsonPropertyName("source")] + public ModelProviderProvenanceSource Source { get; set; } } -/// The input-required variant of . +/// A normalized model-provider adapter in the session's effective catalog. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthLoginStepInputRequired : AuthLoginStep +public sealed class ModelProviderAdapterDescriptor { - /// - [JsonIgnore] - public override string Kind => "input-required"; + /// Stable opaque identity for routing to this adapter. Unique in the effective catalog, independent of live registration generations. + [JsonPropertyName("adapterId")] + public string AdapterId { get; set; } = string.Empty; - /// Prompt for the value the provider needs; the consumer supplies it as advance input (e.g. a GitHub Enterprise Cloud host, *.ghe.com). - [JsonPropertyName("prompt")] - public required string Prompt { get; set; } + /// Adapter-declared policy for passive and automatic discovery. + [JsonPropertyName("automaticDiscovery")] + public ModelProviderAutomaticDiscoveryPolicy AutomaticDiscovery { get => field ??= new(); set; } + + /// Human-readable provider name. + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } = string.Empty; + + /// Operations supported by this provider adapter. + [JsonPropertyName("operations")] + public IList Operations { get => field ??= []; set; } + + /// Attribution for the adapter itself. + [JsonPropertyName("provenance")] + public ModelProviderAttribution Provenance { get => field ??= new(); set; } + + /// Descriptive provider family, such as `ollama`. Different adapters may have the same family; use adapterId for routing. + [JsonPropertyName("providerKind")] + public string ProviderKind { get; set; } = string.Empty; } -/// The awaiting variant of . +/// Normalized model-provider adapter definitions available to the session, not discovered instances. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthLoginStepAwaiting : AuthLoginStep +public sealed class ModelProviderAdapterCatalog { - /// - [JsonIgnore] - public override string Kind => "awaiting"; + /// Available provider adapters ordered by adapterId. + [JsonPropertyName("providers")] + public IList Providers { get => field ??= []; set; } } -/// The needs-interaction variant of . +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthLoginStepNeedsInteraction : AuthLoginStep +internal sealed class SessionProvidersGetCatalogRequest { - /// - [JsonIgnore] - public override string Kind => "needs-interaction"; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// A credential-free account choice after sign-in. +/// Attribution for the adapter that produced a provider row. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AuthLoginAccount +public sealed class ModelProviderProvenance { - /// Host coordinate owned by the selected account's provider. - [JsonPropertyName("host")] - public string Host { get; set; } = string.Empty; + /// Stable opaque adapter identity from the effective catalog. Treat this as a whole identifier, not a parseable owner or kind. + [JsonPropertyName("adapterId")] + public string AdapterId { get; set; } = string.Empty; - /// Provider kind that owns this account choice. - [JsonPropertyName("kind")] - public AccountKind Kind { get; set; } + /// Human-readable contributor name, not the adapter display name. + [JsonPropertyName("ownerDisplayName")] + public string? OwnerDisplayName { get; set; } - /// Human-readable login for the account choice. - [JsonPropertyName("login")] - public string Login { get; set; } = string.Empty; + /// Stable contributor identifier when the adapter has an owner outside the runtime. Independent of the contribution mechanism and not a routing key. + [JsonPropertyName("ownerId")] + public string? OwnerId { get; set; } - /// Opaque identifier supplied to the next login step to select this account. - [JsonPropertyName("selectionId")] - public string SelectionId { get; set; } = string.Empty; + /// Descriptive provider family that produced this row; not a routing key. + [JsonPropertyName("providerKind")] + public string ProviderKind { get; set; } = string.Empty; + + /// Kind of component that supplied the adapter. + [JsonPropertyName("source")] + public ModelProviderProvenanceSource Source { get; set; } } -/// Result of an interactive login flow. Pending consent or account selection is not terminal. +/// Serializable reference to a discovered provider instance. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AuthLoginResultDto +public sealed class ModelProviderInstanceReference { - /// Available accounts when sign-in is awaiting account selection, ordered with Microsoft 365 first. - [JsonPropertyName("accounts")] - public IList? Accounts { get; set; } + /// Stable opaque identity of the adapter that owns this reference. Must be present in the target session's effective catalog. + [JsonPropertyName("adapterId")] + public string AdapterId { get; set; } = string.Empty; - /// Host that was signed in, when completed. + /// Stable instance identifier derived by the provider adapter, such as `ollama:{normalizedEndpoint}`. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Absolute provider management URI. The adapter validates normalization, supported schemes, and permission to access it against its bound configuration; a reference does not grant authority. [Url] [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("host")] - public string? Host { get; set; } - - /// Login that was signed in, when completed. - [JsonPropertyName("login")] - public string? Login { get; set; } + [JsonPropertyName("managementEndpoint")] + public string ManagementEndpoint { get; set; } = string.Empty; - /// Current disposition of the login, including pending user decisions. - [JsonPropertyName("status")] - public AuthLoginResultStatus Status { get; set; } + /// Descriptive provider family. Must match the selected adapter; not a routing key. + [JsonPropertyName("providerKind")] + public string ProviderKind { get; set; } = string.Empty; } -/// The completed variant of . +/// A normalized model-provider instance discovered by the runtime. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthLoginStepCompleted : AuthLoginStep +public sealed class ModelProviderInstance { - /// - [JsonIgnore] - public override string Kind => "completed"; + /// Human-readable instance name. + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } = string.Empty; - /// Login result. When status is needs-plaintext-consent or needs-account-selection, advance with the user's decision to continue. - [JsonPropertyName("result")] - public required AuthLoginResultDto Result { get; set; } + /// Inference API endpoint when the provider exposes one separately from its management endpoint. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("inferenceEndpoint")] + public string? InferenceEndpoint { get; set; } + + /// Transport to use for inference against this instance. + [JsonPropertyName("inferenceTransport")] + public ProviderEndpointTransport? InferenceTransport { get; set; } + + /// Provider family to use for inference against this instance. + [JsonPropertyName("inferenceType")] + public ProviderEndpointType? InferenceType { get; set; } + + /// Wire API to use for inference against this instance, when required by the provider family. + [JsonPropertyName("inferenceWireApi")] + public ProviderEndpointWireApi? InferenceWireApi { get; set; } + + /// Attribution for the adapter that produced this instance. + [JsonPropertyName("provenance")] + public ModelProviderProvenance Provenance { get => field ??= new(); set; } + + /// Self-contained reference for subsequent provider operations. + [JsonPropertyName("reference")] + public ModelProviderInstanceReference Reference { get => field ??= new(); set; } } -/// The error variant of . +/// Typed provider-operation outcome. Use the code for control flow and the optional message for display. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthLoginStepError : AuthLoginStep +public sealed class ModelProviderOperationOutcome { - /// - [JsonIgnore] - public override string Kind => "error"; + /// Machine-readable operation outcome. + [JsonPropertyName("code")] + public ModelProviderOperationOutcomeCode Code { get; set; } - /// Human-readable failure message. + /// Human-readable detail for non-success outcomes. [JsonPropertyName("message")] - public required string Message { get; set; } + public string? Message { get; set; } } -/// A started login flow: its opaque id and first step. +/// Provider instances found by a discovery operation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AuthLoginBegun +public sealed class ModelProviderDiscoverResult { - /// Opaque flow id used to advance or cancel this login. - [JsonPropertyName("flowId")] - public string FlowId { get; set; } = string.Empty; + /// Discovered provider instances. Empty when passive default discovery finds no reachable provider. + [JsonPropertyName("instances")] + public IList Instances { get => field ??= []; set; } - /// The first step of the flow. - [JsonPropertyName("step")] - public AuthLoginStep Step { get => field ??= new(); set; } + /// Typed operation outcome. Passive discovery can return `absent` with an empty instance list. + [JsonPropertyName("outcome")] + public ModelProviderOperationOutcome Outcome { get => field ??= new(); set; } } -/// Begin an interactive login flow for a provider kind. Dispatch is kind-only. +/// Provider discovery parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AuthLoginBeginRequest +internal sealed class ModelProviderDiscoverRequest { - /// The provider kind to sign in with. - [JsonPropertyName("kind")] - public LoginProviderKind Kind { get; set; } + /// Opaque adapter identity returned by `session.providers.getCatalog`. + [JsonPropertyName("adapterId")] + public string AdapterId { get; set; } = string.Empty; + + /// Provider-specific JSON input. Omission or null selects adapter defaults unless requiresInput is true. Non-null input is validated against the advertised Draft 7 schema when present; otherwise validation belongs to the adapter. + [JsonPropertyName("input")] + public JsonElement? Input { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Advance an in-flight login flow, optionally fulfilling an input-required step. +/// Current health information for a provider instance. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AuthLoginAdvanceRequest +public sealed class ModelProviderStatus { - /// Opaque flow id from begin. - [JsonPropertyName("flowId")] - public string FlowId { get; set; } = string.Empty; + /// Normalized provider instance. + [JsonPropertyName("instance")] + public ModelProviderInstance Instance { get => field ??= new(); set; } - /// Neutral input fulfilling a preceding input-required step (e.g. a GHEC host); ignored otherwise. - [JsonPropertyName("input")] - public string? Input { get; set; } + /// Typed operation outcome. + [JsonPropertyName("outcome")] + public ModelProviderOperationOutcome Outcome { get => field ??= new(); set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Open provider status value, such as `healthy`, `unreachable`, or `notInstalled`. + [JsonPropertyName("status")] + public string Status { get; set; } = string.Empty; + + /// Provider-reported version. + [JsonPropertyName("version")] + public string? Version { get; set; } } -/// Cancel an in-flight login flow. +/// Provider status request parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AuthLoginCancelRequest +internal sealed class ModelProviderGetStatusRequest { - /// Opaque flow id from begin. - [JsonPropertyName("flowId")] - public string FlowId { get; set; } = string.Empty; + /// Provider instance reference returned by discovery. + [JsonPropertyName("instance")] + public ModelProviderInstanceReference Instance { get => field ??= new(); set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// A file included in the session debug bundle. +/// Provider-reported model artifact metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DebugCollectLogsCollectedEntry +public sealed class ModelArtifactDetails { - /// Relative path of the file in the staged bundle/archive. - [JsonPropertyName("bundlePath")] - public string BundlePath { get; set; } = string.Empty; + /// Provider-reported model architecture. + [JsonPropertyName("architecture")] + public string? Architecture { get; set; } - /// Redacted output size in bytes. - [JsonPropertyName("sizeBytes")] - public long SizeBytes { get; set; } + /// Provider-reported model families. + [JsonPropertyName("families")] + public IList? Families { get; set; } - /// Source category for this entry. - [JsonPropertyName("source")] - public DebugCollectLogsSource Source { get; set; } + /// Primary model family. + [JsonPropertyName("family")] + public string? Family { get; set; } + + /// Artifact format, such as `gguf`. + [JsonPropertyName("format")] + public string? Format { get; set; } + + /// Provider-reported parameter count label. + [JsonPropertyName("parameterSize")] + public string? ParameterSize { get; set; } + + /// Provider-reported quantization label. + [JsonPropertyName("quantization")] + public string? Quantization { get; set; } + + /// Provider-reported tokenizer. + [JsonPropertyName("tokenizer")] + public string? Tokenizer { get; set; } } -/// An optional debug bundle entry that could not be included. +/// A non-fatal provider observation warning. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DebugCollectLogsSkippedEntry +public sealed class ModelProviderWarning { - /// Relative path requested for this bundle entry. - [JsonPropertyName("bundlePath")] - public string BundlePath { get; set; } = string.Empty; - - /// Server-local source path that could not be read. - [JsonPropertyName("path")] - public string? Path { get; set; } + /// Machine-readable warning code. + [JsonPropertyName("code")] + public string Code { get; set; } = string.Empty; - /// Reason the entry was skipped. - [JsonPropertyName("reason")] - public string Reason { get; set; } = string.Empty; + /// Human-readable warning message. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; } -/// Result of collecting a session debug bundle. +/// A model offered for agent conversations. Missing capability metadata does not disqualify a candidate. Models known to be incompatible, such as embedding-only models, are excluded by the adapter. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DebugCollectLogsResult +public sealed class DiscoveredModel { - /// Files included in the bundle. - [JsonPropertyName("entries")] - public IList Entries { get => field ??= []; set; } + /// Provider-reported model capabilities. Omitted capability fields are unknown; explicit false values are preserved. + [JsonPropertyName("capabilities")] + public ModelCapabilities Capabilities { get => field ??= new(); set; } - /// Destination kind that was written. - [JsonPropertyName("kind")] - public DebugCollectLogsResultKind Kind { get; set; } + /// Provider-reported model artifact details. + [JsonPropertyName("details")] + public ModelArtifactDetails Details { get => field ??= new(); set; } - /// Actual archive path or staging directory path written. This may differ from the requested path when no-overwrite suffixing or fallback-to-temp-directory was needed. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Provider-reported artifact digest. + [JsonPropertyName("digest")] + public string? Digest { get; set; } - /// Optional files or directories that could not be included. - [JsonPropertyName("skippedEntries")] - public IList? SkippedEntries { get; set; } -} + /// Provider-native model identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; -/// A caller-provided server-local file or directory to include in the debug bundle. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DebugCollectLogsEntry -{ - /// Relative path to use inside the staged bundle/archive. - [JsonPropertyName("bundlePath")] - public string BundlePath { get; set; } = string.Empty; + /// Provider-reported last-modified timestamp. + [JsonPropertyName("modifiedAt")] + public DateTimeOffset? ModifiedAt { get; set; } - /// Kind of source path to include. - [JsonPropertyName("kind")] - public DebugCollectLogsEntryKind Kind { get; set; } + /// Provider-reported display name. + [JsonPropertyName("name")] + public string? Name { get; set; } - /// Server-local source path to read. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Attribution for the adapter that produced this model row. + [JsonPropertyName("provenance")] + public ModelProviderProvenance Provenance { get => field ??= new(); set; } - /// How text content from this entry should be redacted. Defaults to plain-text. With none, no redaction is applied; the caller must ensure any necessary redaction is performed before this call. - [JsonPropertyName("redaction")] - public DebugCollectLogsRedaction? Redaction { get; set; } + /// Provider-reported artifact size in bytes. + [JsonPropertyName("sizeBytes")] + public long? SizeBytes { get; set; } - /// When true, collection fails if this entry cannot be read. Defaults to false, which records the entry in `skippedEntries`. - [JsonPropertyName("required")] - public bool? Required { get; set; } + /// Non-fatal warnings encountered while enriching this model. + [JsonPropertyName("warnings")] + public IList Warnings { get => field ??= []; set; } } -/// Destination for the session debug bundle. -/// Polymorphic base type discriminated by kind. +/// Models offered for agent conversations by one provider instance. Adapters exclude known-incompatible models, but retain candidates with unknown capabilities. Listing does not guarantee compatibility. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(DebugCollectLogsDestinationArchive), "archive")] -[JsonDerivedType(typeof(DebugCollectLogsDestinationDirectory), "directory")] -public partial class DebugCollectLogsDestination +public sealed class DiscoveredModelList { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// Provider-native models in provider order. + [JsonPropertyName("models")] + public IList Models { get => field ??= []; set; } + /// Typed operation outcome. + [JsonPropertyName("outcome")] + public ModelProviderOperationOutcome Outcome { get => field ??= new(); set; } +} -/// The archive variant of . +/// Provider model inventory request parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class DebugCollectLogsDestinationArchive : DebugCollectLogsDestination +internal sealed class ModelProviderModelsListRequest { - /// - [JsonIgnore] - public override string Kind => "archive"; - - /// When true, create the archive atomically without overwriting an existing file by appending ` (N)` before the extension as needed. Defaults to false. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("noOverwrite")] - public bool? NoOverwrite { get; set; } + /// Provider instance reference returned by discovery. + [JsonPropertyName("instance")] + public ModelProviderInstanceReference Instance { get => field ??= new(); set; } - /// Absolute or server-relative path for the .tgz archive to create. - [JsonPropertyName("outputPath")] - public required string OutputPath { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// The directory variant of . +/// Vision-specific limits. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class DebugCollectLogsDestinationDirectory : DebugCollectLogsDestination +public sealed class ModelCapabilitiesOverrideLimitsVision { - /// - [JsonIgnore] - public override string Kind => "directory"; + /// Maximum image size in bytes. + [JsonPropertyName("max_prompt_image_size")] + public long? MaxPromptImageSize { get; set; } - /// Directory where files should be staged. The directory is created if needed. - [JsonPropertyName("outputDirectory")] - public required string OutputDirectory { get; set; } + /// Maximum number of images per prompt. + [JsonPropertyName("max_prompt_images")] + public long? MaxPromptImages { get; set; } + + /// MIME types the model accepts. + [JsonPropertyName("supported_media_types")] + public IList? SupportedMediaTypes { get; set; } } -/// Built-in session diagnostics to include in the bundle. Omitted fields default to true. +/// Token limits for prompts, outputs, and context window. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DebugCollectLogsInclude +public sealed class ModelCapabilitiesOverrideLimits { - /// Server-local path to the current process log. When set, it is included as `process.log` and its directory is searched for prior logs from the same session. - [JsonPropertyName("currentProcessLogPath")] - public string? CurrentProcessLogPath { get; set; } + /// Maximum total context window size in tokens. + [JsonPropertyName("max_context_window_tokens")] + public long? MaxContextWindowTokens { get; set; } - /// Include the session event log (`events.jsonl`). Defaults to true. - [JsonPropertyName("events")] - public bool? Events { get; set; } - - /// Server-local path to the session's events.jsonl file. Internal callers normally omit this and let the runtime derive it from the session. - [JsonPropertyName("eventsPath")] - public string? EventsPath { get; set; } - - /// Maximum number of previous process logs to include. Defaults to 5. - [JsonPropertyName("previousProcessLogLimit")] - public long? PreviousProcessLogLimit { get; set; } - - /// Server-local process log directory to search when `currentProcessLogPath` is unavailable, useful for collecting logs for inactive sessions. - [JsonPropertyName("processLogDirectory")] - public string? ProcessLogDirectory { get; set; } + /// Maximum number of output/completion tokens. + [JsonPropertyName("max_output_tokens")] + public long? MaxOutputTokens { get; set; } - /// Include process logs for the session. Defaults to true. - [JsonPropertyName("processLogs")] - public bool? ProcessLogs { get; set; } + /// Maximum number of prompt/input tokens. + [JsonPropertyName("max_prompt_tokens")] + public long? MaxPromptTokens { get; set; } - /// Include interactive shell logs written under the session's `shell-logs` directory. Defaults to true. - [JsonPropertyName("shellLogs")] - public bool? ShellLogs { get; set; } + /// Vision-specific limits. + [JsonPropertyName("vision")] + public ModelCapabilitiesOverrideLimitsVision? Vision { get; set; } } -/// Options for collecting a session debug bundle with configurable redaction. +/// Feature flags indicating what the model supports. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class DebugCollectLogsRequest +public sealed class ModelCapabilitiesOverrideSupports { - /// Caller-provided server-local files or directories to include in addition to the runtime's built-in session diagnostics. This lets host applications add their own diagnostics without changing the API shape. - [JsonPropertyName("additionalEntries")] - public IList? AdditionalEntries { get; set; } + /// Resolved Anthropic adaptive-thinking capability — unsupported / optional / required / adaptive_only. 'required' models reject thinking.type='enabled' with HTTP 400 but still accept 'disabled' (e.g. opus-4.7/4.8/5, sonnet-5); 'adaptive_only' models accept nothing but 'adaptive' (e.g. fable, mythos). + [JsonPropertyName("adaptive_thinking")] + public AdaptiveThinkingSupport? AdaptiveThinking { get; set; } - /// Where the bundle should be written. Use `archive` to produce a .tgz, or `directory` to stage files for caller-managed upload/post-processing. - [JsonPropertyName("destination")] - public DebugCollectLogsDestination Destination { get => field ??= new(); set; } + /// Whether this model supports reasoning effort configuration. + [JsonPropertyName("reasoningEffort")] + public bool? ReasoningEffort { get; set; } - /// Which built-in session diagnostics to include. Omitted fields default to true. - [JsonPropertyName("include")] - public DebugCollectLogsInclude? Include { get; set; } + /// Whether this model supports canonical tool calling. + [JsonPropertyName("toolCalls")] + public bool? ToolCalls { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether this model supports vision/image input. + [JsonPropertyName("vision")] + public bool? Vision { get; set; } } -/// Canvas action that the agent or host can invoke. To discover the input schema for a particular action, call the list_canvas_capabilities tool. +/// Optional capability overrides (vision, tool_calls, reasoning, etc.). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CanvasAction +public sealed class ModelCapabilitiesOverride { - /// Description of the action. - [JsonPropertyName("description")] - public string? Description { get; set; } - - /// JSON Schema for the action input. - [JsonPropertyName("inputSchema")] - public JsonElement? InputSchema { get; set; } + /// Token limits for prompts, outputs, and context window. + [JsonPropertyName("limits")] + public ModelCapabilitiesOverrideLimits? Limits { get; set; } - /// Action name exposed by the canvas provider. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Feature flags indicating what the model supports. + [JsonPropertyName("supports")] + public ModelCapabilitiesOverrideSupports? Supports { get; set; } } -/// Canvas available in the current session. +/// RPC data type for ProtocolSystemMessageAppendConfig operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DiscoveredCanvas +public sealed class ProtocolSystemMessageAppendConfig { - /// Actions the agent or host may invoke on an open instance. - [JsonPropertyName("actions")] - public IList? Actions { get; set; } - - /// Provider-local canvas identifier. - [JsonPropertyName("canvasId")] - public string CanvasId { get; set; } = string.Empty; - - /// Short, single-sentence description shown to the agent in canvas catalogs. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; - - /// Human-readable canvas name. - [JsonPropertyName("displayName")] - public string DisplayName { get; set; } = string.Empty; - - /// Owning provider identifier. - [JsonPropertyName("extensionId")] - public string ExtensionId { get; set; } = string.Empty; - - /// Owning extension display name, when available. - [JsonPropertyName("extensionName")] - public string? ExtensionName { get; set; } - - /// Host-local PNG path for the canvas icon, when supplied. - [JsonPropertyName("icon")] - public string? Icon { get; set; } + /// Text appended to the standard system prompt. + [JsonPropertyName("content")] + public string? Content { get; set; } - /// JSON Schema for canvas open input. - [JsonPropertyName("inputSchema")] - public JsonElement? InputSchema { get; set; } + /// Append-mode discriminator. Omission also selects append mode. + [JsonPropertyName("mode")] + public ProtocolAppendMode? Mode { get; set; } } -/// Declared canvases available in this session. +/// RPC data type for SystemMessageBlock operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CanvasList +public sealed class SystemMessageBlock { - /// Declared canvases available in this session. - [JsonPropertyName("canvases")] - public IList Canvases { get => field ??= []; set; } -} + /// Whether providers with explicit prompt caching should place a cache breakpoint after this block. + [JsonPropertyName("cacheBreakpoint")] + public bool? CacheBreakpoint { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionCanvasListRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Text content for this system-message block. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; + + /// Whether the block is static and may be cached independently of dynamic prompt content. + [JsonPropertyName("isStatic")] + public bool? IsStatic { get; set; } } -/// Open canvas instance snapshot. +/// RPC data type for ProtocolSystemMessageReplaceConfig operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class OpenCanvasInstance +public sealed class ProtocolSystemMessageReplaceConfig { - /// Provider-local canvas identifier. - [JsonPropertyName("canvasId")] - public string CanvasId { get; set; } = string.Empty; - - /// Owning provider identifier. - [JsonPropertyName("extensionId")] - public string ExtensionId { get; set; } = string.Empty; - - /// Owning extension display name, when available. - [JsonPropertyName("extensionName")] - public string? ExtensionName { get; set; } - - /// Host-local PNG path for the canvas icon, when supplied. - [JsonPropertyName("icon")] - public string? Icon { get; set; } - - /// Input supplied when the instance was opened. - [JsonPropertyName("input")] - public JsonElement? Input { get; set; } - - /// Stable caller-supplied canvas instance identifier. - [JsonPropertyName("instanceId")] - public string InstanceId { get; set; } = string.Empty; - - /// Provider-supplied status text. - [JsonPropertyName("status")] - public string? Status { get; set; } + /// Complete replacement system-message text. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; - /// Rendered title. - [JsonPropertyName("title")] - public string? Title { get; set; } + /// Optional structured blocks corresponding to the replacement content. + [JsonPropertyName("contentBlocks")] + public IList? ContentBlocks { get; set; } - /// URL for web-rendered canvases. - [JsonPropertyName("url")] - public string? Url { get; set; } + /// Replace-mode discriminator. + [JsonPropertyName("mode")] + public ProtocolReplaceMode Mode { get; set; } } -/// Live open-canvas snapshot. +/// RPC data type for ProtocolStaticSectionOverride operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CanvasListOpenResult +public sealed class ProtocolStaticSectionOverride { - /// Currently open canvas instances. - [JsonPropertyName("openCanvases")] - public IList OpenCanvases { get => field ??= []; set; } -} + /// Declarative operation applied to the section. + [JsonPropertyName("action")] + public ProtocolStaticSectionAction Action { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionCanvasListOpenRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Optional content used by replace, append, and prepend operations. + [JsonPropertyName("content")] + public string? Content { get; set; } } -/// Canvas open parameters. +/// Polymorphic base type discriminated by action. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CanvasOpenRequest +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "action", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(ProtocolMarkerSectionOverrideTransform), "transform")] +[JsonDerivedType(typeof(ProtocolMarkerSectionOverridePreserve), "preserve")] +public partial class ProtocolMarkerSectionOverride { - /// Provider-local canvas identifier. - [JsonPropertyName("canvasId")] - public string CanvasId { get; set; } = string.Empty; - - /// Owning provider identifier. Optional when the canvasId is unique across providers; required to disambiguate when multiple providers register the same canvasId. - [JsonPropertyName("extensionId")] - public string? ExtensionId { get; set; } - - /// Canvas open input. - [JsonPropertyName("input")] - public JsonElement? Input { get; set; } - - /// Caller-supplied stable instance identifier. - [JsonPropertyName("instanceId")] - public string InstanceId { get; set; } = string.Empty; - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// The type discriminator. + [JsonPropertyName("action")] + public virtual string Action { get; set; } = string.Empty; } -/// Canvas close parameters. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CanvasCloseRequest -{ - /// Open canvas instance identifier. - [JsonPropertyName("instanceId")] - public string InstanceId { get; set; } = string.Empty; - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} -/// Canvas action invocation result. +/// The transform variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CanvasActionInvokeResult +public partial class ProtocolMarkerSectionOverrideTransform : ProtocolMarkerSectionOverride { - /// Provider-supplied action result. - [JsonPropertyName("result")] - public JsonElement? Result { get; set; } + /// + [JsonIgnore] + public override string Action => "transform"; } -/// Canvas action invocation parameters. +/// The preserve variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CanvasActionInvokeRequest +public partial class ProtocolMarkerSectionOverridePreserve : ProtocolMarkerSectionOverride { - /// Action name to invoke. - [JsonPropertyName("actionName")] - public string ActionName { get; set; } = string.Empty; - - /// Action input. - [JsonPropertyName("input")] - public JsonElement? Input { get; set; } - - /// Open canvas instance identifier. - [JsonPropertyName("instanceId")] - public string InstanceId { get; set; } = string.Empty; - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Action => "preserve"; } -/// Internal canvas provider registration parameters. +/// JSON union data type for ProtocolSectionOverride. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CanvasProviderRegisterRequest +[JsonConverter(typeof(Converter))] +public sealed partial class ProtocolSectionOverride { - /// Canvas contributions supplied by the provider. - [JsonPropertyName("canvases")] - public IList Canvases { get => field ??= []; set; } - - /// Connection identifier for callback routing. - [JsonPropertyName("connectionId")] - public string ConnectionId { get; set; } = string.Empty; - - /// Provider metadata supplied by the host. - [JsonPropertyName("info")] - public JsonElement Info { get; set; } + /// Gets the value when this instance contains . + public ProtocolStaticSectionOverride? ProtocolStaticSectionOverride { get; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Gets the value when this instance contains . + public ProtocolMarkerSectionOverride? ProtocolMarkerSectionOverride { get; } -/// Internal canvas provider unregistration parameters. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CanvasProviderUnregisterRequest -{ - /// Connection identifier to unregister. - [JsonPropertyName("connectionId")] - public string ConnectionId { get; set; } = string.Empty; + /// Initializes a new instance of the class from . + public ProtocolSectionOverride(ProtocolStaticSectionOverride value) + { + ArgumentNullException.ThrowIfNull(value); + ProtocolStaticSectionOverride = value; + } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Converts to . + public static implicit operator ProtocolSectionOverride(ProtocolStaticSectionOverride value) => new(value); -/// Machine-readable workflow run failure. -/// Polymorphic base type discriminated by type. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "type", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(WorkflowRunFailureWorkflowLimitReached), "workflow_limit_reached")] -[JsonDerivedType(typeof(WorkflowRunFailureWorkflowResumeDeclined), "workflow_resume_declined")] -[JsonDerivedType(typeof(WorkflowRunFailureWorkflowDurableFailure), "workflow_durable_failure")] -[JsonDerivedType(typeof(WorkflowRunFailureWorkflowAccountingIncomplete), "workflow_accounting_incomplete")] -[JsonDerivedType(typeof(WorkflowRunFailureWorkflowProviderDisconnected), "workflow_provider_disconnected")] -public partial class WorkflowRunFailure -{ - /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { get; set; } = string.Empty; -} + /// Initializes a new instance of the class from . + public ProtocolSectionOverride(ProtocolMarkerSectionOverride value) + { + ArgumentNullException.ThrowIfNull(value); + ProtocolMarkerSectionOverride = value; + } + /// Converts to . + public static implicit operator ProtocolSectionOverride(ProtocolMarkerSectionOverride value) => new(value); -/// The workflow_limit_reached variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class WorkflowRunFailureWorkflowLimitReached : WorkflowRunFailure -{ - /// - [JsonIgnore] - public override string Type => "workflow_limit_reached"; + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ProtocolSectionOverride Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) + { + throw new JsonException("Expected JSON object for ProtocolSectionOverride."); + } - /// Resource ceiling that stopped the run. - [JsonPropertyName("kind")] - public required WorkflowRunFailureKind Kind { get; set; } + using var document = JsonDocument.ParseValue(ref reader); + var element = document.RootElement; + if (element.ValueKind == JsonValueKind.Object && (element.TryGetProperty("action", out _) && element.GetProperty("action").ValueKind == JsonValueKind.String && (element.GetProperty("action").GetString() == "replace" || element.GetProperty("action").GetString() == "remove" || element.GetProperty("action").GetString() == "append" || element.GetProperty("action").GetString() == "prepend"))) + { + var protocolStaticSectionOverride = JsonSerializer.Deserialize(element, RpcJsonContext.Default.ProtocolStaticSectionOverride); + return protocolStaticSectionOverride is null ? throw new JsonException("Expected ProtocolStaticSectionOverride value.") : new ProtocolSectionOverride(protocolStaticSectionOverride); + } + if ((element.ValueKind == JsonValueKind.Object && (element.TryGetProperty("action", out _) && element.GetProperty("action").ValueKind == JsonValueKind.String && (element.GetProperty("action").GetString() == "transform")) || element.ValueKind == JsonValueKind.Object && (element.TryGetProperty("action", out _) && element.GetProperty("action").ValueKind == JsonValueKind.String && (element.GetProperty("action").GetString() == "preserve")))) + { + var protocolMarkerSectionOverride = JsonSerializer.Deserialize(element, RpcJsonContext.Default.ProtocolMarkerSectionOverride); + return protocolMarkerSectionOverride is null ? throw new JsonException("Expected ProtocolMarkerSectionOverride value.") : new ProtocolSectionOverride(protocolMarkerSectionOverride); + } - /// Workflow run identifier. - [JsonPropertyName("runId")] - public required string RunId { get; set; } + throw new JsonException("JSON value did not match any ProtocolSectionOverride variant."); + } - /// Suggested larger ceiling when the runtime can derive one safely. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("suggestedValue")] - public double? SuggestedValue { get; set; } + /// + public override void Write(Utf8JsonWriter writer, ProtocolSectionOverride value, JsonSerializerOptions options) + { + if (value.ProtocolStaticSectionOverride is { } protocolStaticSectionOverride) + { + JsonSerializer.Serialize(writer, protocolStaticSectionOverride, RpcJsonContext.Default.ProtocolStaticSectionOverride); + return; + } + if (value.ProtocolMarkerSectionOverride is { } protocolMarkerSectionOverride) + { + JsonSerializer.Serialize(writer, protocolMarkerSectionOverride, RpcJsonContext.Default.ProtocolMarkerSectionOverride); + return; + } - /// Approved effective ceiling that was reached. - [JsonPropertyName("value")] - public required double Value { get; set; } + throw new JsonException("No ProtocolSectionOverride variant value is set."); + } + } } -/// The workflow_resume_declined variant of . +/// RPC data type for ProtocolSystemMessageCustomizeConfig operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class WorkflowRunFailureWorkflowResumeDeclined : WorkflowRunFailure +public sealed class ProtocolSystemMessageCustomizeConfig { - /// - [JsonIgnore] - public override string Type => "workflow_resume_declined"; + /// Text appended after the customized sections. + [JsonPropertyName("content")] + public string? Content { get; set; } - /// Human-readable reason the resume did not proceed. - [JsonPropertyName("reason")] - public required string Reason { get; set; } + /// Customize-mode discriminator. + [JsonPropertyName("mode")] + public ProtocolCustomizeMode Mode { get; set; } - /// Workflow run identifier whose changed limits were declined. - [JsonPropertyName("runId")] - public required string RunId { get; set; } + /// Named standard-prompt section overrides. + [JsonPropertyName("sections")] + public IDictionary? Sections { get; set; } } -/// The workflow_durable_failure variant of . +/// JSON union data type for ProtocolSystemMessageConfig. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class WorkflowRunFailureWorkflowDurableFailure : WorkflowRunFailure +[JsonConverter(typeof(Converter))] +public sealed partial class ProtocolSystemMessageConfig { - /// - [JsonIgnore] - public override string Type => "workflow_durable_failure"; + /// Gets the value when this instance contains . + public ProtocolSystemMessageAppendConfig? ProtocolSystemMessageAppendConfig { get; } - /// Stable failure code. - [JsonPropertyName("code")] - public required string Code { get; set; } + /// Gets the value when this instance contains . + public ProtocolSystemMessageReplaceConfig? ProtocolSystemMessageReplaceConfig { get; } - /// Execution-critical durable operation that failed. - [JsonPropertyName("operation")] - public required WorkflowDurableOperation Operation { get; set; } + /// Gets the value when this instance contains . + public ProtocolSystemMessageCustomizeConfig? ProtocolSystemMessageCustomizeConfig { get; } - /// Workflow run identifier. - [JsonPropertyName("runId")] - public required string RunId { get; set; } -} + /// Initializes a new instance of the class from . + public ProtocolSystemMessageConfig(ProtocolSystemMessageAppendConfig value) + { + ArgumentNullException.ThrowIfNull(value); + ProtocolSystemMessageAppendConfig = value; + } -/// The run stopped because its usage accounting could not be completed. -/// The workflow_accounting_incomplete variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class WorkflowRunFailureWorkflowAccountingIncomplete : WorkflowRunFailure -{ - /// - [JsonIgnore] - public override string Type => "workflow_accounting_incomplete"; + /// Converts to . + public static implicit operator ProtocolSystemMessageConfig(ProtocolSystemMessageAppendConfig value) => new(value); - /// Confirmed usage in nano-AIU, representing the floor of what the run spent. - [JsonPropertyName("drainedNanoAiu")] - public required long DrainedNanoAiu { get; set; } + /// Initializes a new instance of the class from . + public ProtocolSystemMessageConfig(ProtocolSystemMessageReplaceConfig value) + { + ArgumentNullException.ThrowIfNull(value); + ProtocolSystemMessageReplaceConfig = value; + } - /// Workflow run identifier. - [JsonPropertyName("runId")] - public required string RunId { get; set; } -} + /// Converts to . + public static implicit operator ProtocolSystemMessageConfig(ProtocolSystemMessageReplaceConfig value) => new(value); -/// The extension that owns the workflow disconnected while the run was executing, so the host halted it. The run's journaled subagent results are preserved so a resume can reuse them. -/// The workflow_provider_disconnected variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class WorkflowRunFailureWorkflowProviderDisconnected : WorkflowRunFailure -{ - /// - [JsonIgnore] - public override string Type => "workflow_provider_disconnected"; + /// Initializes a new instance of the class from . + public ProtocolSystemMessageConfig(ProtocolSystemMessageCustomizeConfig value) + { + ArgumentNullException.ThrowIfNull(value); + ProtocolSystemMessageCustomizeConfig = value; + } - /// Workflow run identifier. - [JsonPropertyName("runId")] - public required string RunId { get; set; } -} + /// Converts to . + public static implicit operator ProtocolSystemMessageConfig(ProtocolSystemMessageCustomizeConfig value) => new(value); -/// Durable metadata describing who initiated a workflow pause. -/// Polymorphic base type discriminated by type. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "type", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(WorkflowPauseInfoUser), "user")] -[JsonDerivedType(typeof(WorkflowPauseInfoCheckpoint), "checkpoint")] -public partial class WorkflowPauseInfo -{ - /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { get; set; } = string.Empty; -} + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ProtocolSystemMessageConfig Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) + { + throw new JsonException("Expected JSON object for ProtocolSystemMessageConfig."); + } + + using var document = JsonDocument.ParseValue(ref reader); + var element = document.RootElement; + if (element.ValueKind == JsonValueKind.Object && (!element.TryGetProperty("mode", out _) || (element.TryGetProperty("mode", out _) && element.GetProperty("mode").ValueKind == JsonValueKind.String && (element.GetProperty("mode").GetString() == "append")))) + { + var protocolSystemMessageAppendConfig = JsonSerializer.Deserialize(element, RpcJsonContext.Default.ProtocolSystemMessageAppendConfig); + return protocolSystemMessageAppendConfig is null ? throw new JsonException("Expected ProtocolSystemMessageAppendConfig value.") : new ProtocolSystemMessageConfig(protocolSystemMessageAppendConfig); + } + if (element.ValueKind == JsonValueKind.Object && (element.TryGetProperty("mode", out _) && element.GetProperty("mode").ValueKind == JsonValueKind.String && (element.GetProperty("mode").GetString() == "replace"))) + { + var protocolSystemMessageReplaceConfig = JsonSerializer.Deserialize(element, RpcJsonContext.Default.ProtocolSystemMessageReplaceConfig); + return protocolSystemMessageReplaceConfig is null ? throw new JsonException("Expected ProtocolSystemMessageReplaceConfig value.") : new ProtocolSystemMessageConfig(protocolSystemMessageReplaceConfig); + } + if (element.ValueKind == JsonValueKind.Object && (element.TryGetProperty("mode", out _) && element.GetProperty("mode").ValueKind == JsonValueKind.String && (element.GetProperty("mode").GetString() == "customize"))) + { + var protocolSystemMessageCustomizeConfig = JsonSerializer.Deserialize(element, RpcJsonContext.Default.ProtocolSystemMessageCustomizeConfig); + return protocolSystemMessageCustomizeConfig is null ? throw new JsonException("Expected ProtocolSystemMessageCustomizeConfig value.") : new ProtocolSystemMessageConfig(protocolSystemMessageCustomizeConfig); + } + throw new JsonException("JSON value did not match any ProtocolSystemMessageConfig variant."); + } -/// The user variant of . + /// + public override void Write(Utf8JsonWriter writer, ProtocolSystemMessageConfig value, JsonSerializerOptions options) + { + if (value.ProtocolSystemMessageAppendConfig is { } protocolSystemMessageAppendConfig) + { + JsonSerializer.Serialize(writer, protocolSystemMessageAppendConfig, RpcJsonContext.Default.ProtocolSystemMessageAppendConfig); + return; + } + if (value.ProtocolSystemMessageReplaceConfig is { } protocolSystemMessageReplaceConfig) + { + JsonSerializer.Serialize(writer, protocolSystemMessageReplaceConfig, RpcJsonContext.Default.ProtocolSystemMessageReplaceConfig); + return; + } + if (value.ProtocolSystemMessageCustomizeConfig is { } protocolSystemMessageCustomizeConfig) + { + JsonSerializer.Serialize(writer, protocolSystemMessageCustomizeConfig, RpcJsonContext.Default.ProtocolSystemMessageCustomizeConfig); + return; + } + + throw new JsonException("No ProtocolSystemMessageConfig variant value is set."); + } + } +} + +/// A BYOK model definition referencing a named provider. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class WorkflowPauseInfoUser : WorkflowPauseInfo +public sealed class ProviderModelConfig { - /// - [JsonIgnore] - public override string Type => "user"; + /// Optional capability overrides (vision, tool_calls, reasoning, etc.). + [JsonPropertyName("capabilities")] + public ModelCapabilitiesOverride? Capabilities { get; set; } + + /// Provider-local model id, unique within its provider. The session-wide selection id (shown in the model list and passed to switchTo) is the provider-qualified `provider/id`. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Maximum context window tokens for the model. + [JsonPropertyName("maxContextWindowTokens")] + public double? MaxContextWindowTokens { get; set; } + + /// Maximum output tokens for the model. + [JsonPropertyName("maxOutputTokens")] + public double? MaxOutputTokens { get; set; } + + /// Maximum prompt/input tokens for the model. + [JsonPropertyName("maxPromptTokens")] + public double? MaxPromptTokens { get; set; } + + /// Provider-published model metadata, preserved verbatim as the public Model.metadata object. + [JsonPropertyName("metadata")] + public IDictionary? Metadata { get; set; } + + /// Well-known base model id used for behavior/capability/config lookup. Defaults to `id`. + [JsonPropertyName("modelId")] + public string? ModelId { get; set; } + + /// Display name for model pickers. Defaults to the provider-qualified selection id (`provider/id`). + [JsonPropertyName("name")] + public string? Name { get; set; } + + /// Name of the configured provider that serves this model. + [JsonPropertyName("provider")] + public string Provider { get; set; } = string.Empty; + + /// System-message configuration used when the runtime builds the standard prompt for this provider-qualified model, including general-purpose subagents. It uses the same object hierarchy as session-level systemMessage configuration, except transform actions are rejected because the current callback protocol is not model-scoped. When present, it overrides the session-wide configuration on those prompt paths. Selected custom-agent and specialized-subagent prompts remain authoritative. + [JsonPropertyName("systemMessage")] + public ProtocolSystemMessageConfig? SystemMessage { get; set; } + + /// The model name sent to the provider API for inference. Defaults to `id`. + [JsonPropertyName("wireModel")] + public string? WireModel { get; set; } } -/// The checkpoint variant of . +/// Azure-specific provider options. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class WorkflowPauseInfoCheckpoint : WorkflowPauseInfo +public sealed class ProviderConfigAzure { - /// - [JsonIgnore] - public override string Type => "checkpoint"; - - /// Stable author-defined checkpoint key that initiated the pause. - [JsonPropertyName("key")] - public required string Key { get; set; } + /// API version. When set, uses the versioned deployment route. When omitted, uses the GA versionless v1 route. + [JsonPropertyName("apiVersion")] + public string? ApiVersion { get; set; } } -/// Complete current or terminal workflow run envelope. +/// External SDK input for a named custom model provider. Ingested by the native protocol boundary before host dispatch. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowRunResult +public sealed class NamedProviderConfig { - /// One-based execution attempt represented by this envelope. Absent before the first attempt starts or when returned by an older runtime. - [JsonPropertyName("attempt")] - public long? Attempt { get; set; } + /// Static API key used to authenticate provider requests. + [JsonPropertyName("apiKey")] + public string? ApiKey { get; set; } - /// Error message for an errored run. - [JsonPropertyName("error")] - public string? Error { get; set; } + /// Azure authentication configuration for the provider. + [JsonPropertyName("azure")] + public ProviderConfigAzure? Azure { get; set; } - /// Machine-readable failure details for a halted or errored run. - [JsonPropertyName("failure")] - public WorkflowRunFailure? Failure { get; set; } + /// Base URL for provider API requests. + [JsonPropertyName("baseUrl")] + public string BaseUrl { get; set; } = string.Empty; - /// Structured pause initiator metadata for a paused attempt. - [JsonPropertyName("pauseInfo")] - public WorkflowPauseInfo? PauseInfo { get; set; } + /// Static bearer token used to authenticate provider requests. + [JsonPropertyName("bearerToken")] + public string? BearerToken { get; set; } - /// Reason for a halted or cancelled run. - [JsonPropertyName("reason")] - public string? Reason { get; set; } + /// Whether the host supplies bearer tokens dynamically. + [JsonPropertyName("hasBearerTokenProvider")] + public bool? HasBearerTokenProvider { get; set; } - /// Completed workflow result. - [JsonPropertyName("result")] - public JsonElement? Result { get; set; } + /// Additional HTTP headers included with provider requests. + [JsonPropertyName("headers")] + public IDictionary? Headers { get; set; } - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// The product serving the provider's models, reported in telemetry as `model_provider`. Only affects telemetry. + [JsonPropertyName("modelProvider")] + public ProviderConfigModelProvider? ModelProvider { get; set; } - /// Partial journal and progress snapshot for a halted, cancelled, or errored run. - [JsonPropertyName("snapshot")] - public JsonElement? Snapshot { get; set; } + /// Unique provider name used to qualify model selection IDs. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Current or terminal workflow run status. - [JsonPropertyName("status")] - public WorkflowRunStatus Status { get; set; } + /// Transport used to communicate with the provider. + [JsonPropertyName("transport")] + public ProviderConfigTransport? Transport { get; set; } + + /// Provider protocol family. + [JsonPropertyName("type")] + public ProviderConfigType? Type { get; set; } + + /// Wire API used to communicate with the provider. + [JsonPropertyName("wireApi")] + public ProviderConfigWireApi? WireApi { get; set; } } -/// Wire-only per-invocation workflow resource ceiling overrides. +/// Provider configuration prepared from a discovered model. Preparing a plan changes nothing: it neither registers the model with the session nor writes durable configuration. To apply it, pass `provider` and `model` to `session.provider.add`, omitting whichever the dispositions report as already configured. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowRunLimits +public sealed class ModelProviderConfigurationPlan { - /// Maximum AI credits consumed by workflow subagents and their descendants. The post-paid ceiling is soft: parallel turns can settle beyond it before the run stops. - [JsonPropertyName("maxAiCredits")] - public double? MaxAiCredits { get; set; } + /// Model definition prepared from the discovered model. Capability fields the provider did not report stay omitted rather than being asserted false. + [JsonPropertyName("model")] + public ProviderModelConfig Model { get => field ??= new(); set; } - /// Maximum number of workflow subagents that may run concurrently. - [JsonPropertyName("maxConcurrentSubagents")] - public long? MaxConcurrentSubagents { get; set; } + /// Whether `model` still needs to be registered. When `alreadyConfigured`, `selectionId` is already registered and the caller can select it without adding anything. + [JsonPropertyName("modelDisposition")] + public ModelProviderConfigurationDisposition ModelDisposition { get; set; } - /// Maximum total number of workflow subagents that may be admitted. - [JsonPropertyName("maxTotalSubagents")] - public long? MaxTotalSubagents { get; set; } + /// Provider connection prepared from the instance's inference metadata. Carries no credential; supply one if the endpoint requires it. + [JsonPropertyName("provider")] + public NamedProviderConfig Provider { get => field ??= new(); set; } - /// Maximum accumulated active-execution time in seconds. Active execution includes the entire extension body, subprocess waits, queued-agent waits, and sleeps; time between resumed attempts is not counted. - [JsonPropertyName("timeoutSeconds")] - public double? TimeoutSeconds { get; set; } + /// Whether `provider` still needs to be registered. When `alreadyConfigured`, a provider with the same endpoint is already registered and `provider` restates it under its existing name; adding it again is rejected as a duplicate. + [JsonPropertyName("providerDisposition")] + public ModelProviderConfigurationDisposition ProviderDisposition { get; set; } + + /// Provider-qualified selection id (`provider/id`) to pass to `switchTo` once the plan is applied. + [JsonPropertyName("selectionId")] + public string SelectionId { get; set; } = string.Empty; + + /// Non-fatal warnings carried over from the discovered model, such as capabilities the provider did not report. + [JsonPropertyName("warnings")] + public IList Warnings { get => field ??= []; set; } } -/// Options controlling workflow invocation. +/// A discovered instance and one of its models to translate into provider configuration. Pass back the instance and model as returned by `session.providers.discover` and `session.providers.models.list`. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowRunOptions +internal sealed class ModelProviderPrepareConfigurationRequest { - /// Per-invocation resource ceiling overrides. - [JsonPropertyName("limits")] - public WorkflowRunLimits? Limits { get; set; } - - /// Whether to emit workflow phase names to the session transcript. - [JsonPropertyName("logPhaseNames")] - public bool? LogPhaseNames { get; set; } + /// The discovered instance that serves the model. + [JsonPropertyName("instance")] + public ModelProviderInstance Instance { get => field ??= new(); set; } - /// Whether to notify the originating session when the workflow completes. - [JsonPropertyName("notifyOnComplete")] - public bool? NotifyOnComplete { get; set; } + /// The discovered model to configure. + [JsonPropertyName("model")] + public DiscoveredModel Model { get => field ??= new(); set; } - /// Run identifier whose journal and progress should seed this resumed run. - [JsonPropertyName("resumeFromRunId")] - public string? ResumeFromRunId { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Parameters for invoking a registered workflow. +/// Managed sandbox enforcement state for a session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowRunRequest +public sealed class SandboxEnforcementStatus { - /// Workflow input value. - [JsonPropertyName("args")] - public JsonElement Args { get; set; } + /// Whether an enforcement failure has permanently blocked the session. + [JsonPropertyName("blocked")] + public bool Blocked { get; set; } - /// Registered workflow name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// The first sandbox enforcement failure that blocked the session. + [JsonPropertyName("reason")] + public string? Reason { get; set; } - /// Workflow invocation options. - [JsonPropertyName("options")] - public WorkflowRunOptions? Options { get; set; } + /// Whether the effective managed policy requires an available sandbox backend. + [JsonPropertyName("required")] + public bool Required { get; set; } +} +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionSandboxGetEnforcementStatusRequest +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Resolved persisted workflow identity and resumed run envelope. +/// A possible secret-bearing environment variable and optional locally suggested HTTPS injection hosts. This is a draft for user review, not an active grant. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowResumeResult +public sealed class SandboxCredentialSuggestion { - /// Terminal resumed run envelope. - [JsonPropertyName("run")] - public WorkflowRunResult Run { get => field ??= new(); set; } + /// Environment variable name. The secret value is never returned. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Persisted workflow name resolved for the resumed run. - [JsonPropertyName("workflowName")] - public string WorkflowName { get; set; } = string.Empty; + /// HTTPS hostnames suggested by a local known-provider mapping. An empty list requires the user to supply injection hosts before adding a masking entry. These suggestions do not allow network access. + [JsonPropertyName("suggestedInjectHosts")] + public IList SuggestedInjectHosts { get => field ??= []; set; } } -/// Parameters for resuming a workflow run from its persisted identity. +/// Possible unconfigured secrets in the sandbox shell environment, sorted by variable name, with no secret values. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowResumeRequest +public sealed class SandboxCredentialSuggestionsResult { - /// Optional per-invocation resource ceiling overrides. - [JsonPropertyName("limits")] - public WorkflowRunLimits? Limits { get; set; } - - /// Whether to emit workflow phase names to the session transcript. - [JsonPropertyName("logPhaseNames")] - public bool? LogPhaseNames { get; set; } - - /// Whether to notify the originating session when the workflow completes. - [JsonPropertyName("notifyOnComplete")] - public bool? NotifyOnComplete { get; set; } - - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// Candidates for user review. Empty when sandboxing is disabled or no unconfigured candidates are present. + [JsonPropertyName("suggestions")] + public IList Suggestions { get => field ??= []; set; } +} +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionSandboxGetCredentialSuggestionsRequest +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Options for an internal tool-originated workflow invocation. +/// Result of attempting to disable sandboxing for the current session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowToolRunOptions +public sealed class SandboxDisableForSessionResult { - /// Per-invocation resource ceiling overrides. - [JsonPropertyName("limits")] - public WorkflowRunLimits? Limits { get; set; } + /// The authoritative sandbox enabled state after the operation. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } - /// Run identifier whose journal and progress should seed this resumed run. - [JsonPropertyName("resumeFromRunId")] - public string? ResumeFromRunId { get; set; } + /// Whether this call resolved the pending request and applied the session opt-out. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Internal parameters for invoking a registered workflow from a tool. +/// Optional informational context describing how and where the permission decision was made. This does not affect permission behavior. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowToolRunRequest +public sealed class PermissionDecisionContext { - /// Workflow input value. - [JsonPropertyName("args")] - public JsonElement Args { get; set; } - - /// Registered workflow name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Disposition of the permission request as observed by the responding client. + [JsonPropertyName("outcome")] + public PermissionDecisionOutcome Outcome { get; set; } - /// Tool-originated workflow invocation options. - [JsonPropertyName("options")] - public WorkflowToolRunOptions? Options { get; set; } + /// Whether the responding client could ask a user interactively, was running headlessly, or had no response path. Omit when the client cannot determine this authoritatively. + [JsonPropertyName("responseCapability")] + public PermissionResponseCapability? ResponseCapability { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Controlled reason or actor responsible for the response. + [JsonPropertyName("source")] + public PermissionDecisionSource Source { get; set; } - /// Opaque identifier of the originating tool call. - [JsonPropertyName("toolCallId")] - public string? ToolCallId { get; set; } + /// Client surface that submitted the response. + [JsonPropertyName("surface")] + public PermissionDecisionSurface Surface { get; set; } } -/// Internal parameters for resuming a workflow run from a tool. +/// Request to disable sandboxing for the current session while resolving an active sandbox-bypass permission prompt. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowToolResumeRequest +internal sealed class SandboxDisableForSessionRequest { - /// Optional per-invocation resource ceiling overrides. - [JsonPropertyName("limits")] - public WorkflowRunLimits? Limits { get; set; } + /// Optional attribution for the permission decision. + [JsonPropertyName("decisionContext")] + public PermissionDecisionContext? DecisionContext { get; set; } - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// Identifier of the exact pending sandbox-bypass permission request that authorized the session opt-out. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; +} - /// Opaque identifier of the originating tool call. - [JsonPropertyName("toolCallId")] - public string? ToolCallId { get; set; } +/// Result of accepting a sandbox path grant. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SandboxGrantPathForRequestResult +{ + /// Whether this call resolved the pending request and added the path to the session's sandbox policy. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Parameters for retrieving a workflow run. +/// Request to accept the sandbox path grant offered on an active sandbox escalation permission prompt. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowGetRunRequest +internal sealed class SandboxGrantPathForRequestRequest { - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// Optional attribution for the permission decision. + [JsonPropertyName("decisionContext")] + public PermissionDecisionContext? DecisionContext { get; set; } + + /// Identifier of the exact pending sandbox escalation permission request whose sandboxPathGrant to accept. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Declared or approved workflow resource ceilings. +/// Authentication status and account metadata for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowDeclaredLimits +public sealed class SessionAuthStatus { - /// Maximum AI credits consumed by subagents and descendants. - [JsonPropertyName("maxAiCredits")] - public double? MaxAiCredits { get; set; } + /// Authentication type. + [JsonPropertyName("authType")] + public AuthInfoType? AuthType { get; set; } - /// Maximum concurrently active subagents. - [JsonPropertyName("maxConcurrentSubagents")] - public long? MaxConcurrentSubagents { get; set; } + /// Copilot plan tier (e.g., individual_pro, business). + [JsonPropertyName("copilotPlan")] + public string? CopilotPlan { get; set; } - /// Maximum total subagents spawned by the run. - [JsonPropertyName("maxTotalSubagents")] - public long? MaxTotalSubagents { get; set; } + /// Authentication host URL. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("host")] + public string? Host { get; set; } - /// Maximum accumulated active execution time in seconds. - [JsonPropertyName("timeoutSeconds")] - public double? TimeoutSeconds { get; set; } + /// Whether the session has resolved authentication. + [JsonPropertyName("isAuthenticated")] + public bool IsAuthenticated { get; set; } + + /// Authenticated login/username, if available. + [JsonPropertyName("login")] + public string? Login { get; set; } + + /// Human-readable authentication status description. + [JsonPropertyName("statusMessage")] + public string? StatusMessage { get; set; } } -/// Durable workflow resource consumption. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowRunConsumed +internal sealed class SessionGitHubAuthGetStatusRequest { - /// Accumulated active execution time in milliseconds. - [JsonPropertyName("activeMs")] - public long ActiveMs { get; set; } - - /// AI usage consumed by the run in nano-AIU. - [JsonPropertyName("nanoAiu")] - public long NanoAiu { get; set; } - - /// Total subagents spawned by the run. - [JsonPropertyName("subagents")] - public long Subagents { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Current workflow phase identity. +/// Indicates whether the credential update succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowCurrentPhase +public sealed class SessionSetCredentialsResult { - /// Current phase identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Whether the session ended up with a populated `copilotUser` for the installed credentials. `true` when the supplied credential already carried `copilotUser` or it was successfully re-resolved server-side. `false` when the credential is installed without `copilotUser` — either re-resolution failed, or the variant cannot be re-resolved from the credential alone (only the raw-token variants `token`, `env`, and `gh-cli` can). In both `false` cases the token swap still applied, but plan/quota/billing metadata is degraded. Present whenever a credential was supplied; omitted only when no credential was supplied (no-op call). + [JsonPropertyName("copilotUserResolved")] + public bool? CopilotUserResolved { get; set; } - /// Zero-based declared phase ordinal, or null for an undeclared phase. - [JsonPropertyName("ordinal")] - public long? Ordinal { get; set; } + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Prompt-safe terminal workflow outcome. +/// Authentication credentials accepted by session.gitHubAuth.setCredentials. Session-owned token-provider identities cannot be installed through this method. +/// Polymorphic base type discriminated by type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowRunTerminal +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "type", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(SettableAuthInfoHmac), "hmac")] +[JsonDerivedType(typeof(SettableAuthInfoEnv), "env")] +[JsonDerivedType(typeof(SettableAuthInfoToken), "token")] +[JsonDerivedType(typeof(SettableAuthInfoCopilotApiToken), "copilot-api-token")] +[JsonDerivedType(typeof(SettableAuthInfoUser), "user")] +[JsonDerivedType(typeof(SettableAuthInfoAccount), "account")] +[JsonDerivedType(typeof(SettableAuthInfoGhCli), "gh-cli")] +[JsonDerivedType(typeof(SettableAuthInfoApiKey), "api-key")] +public partial class SettableAuthInfo { - /// Human-readable terminal error. - [JsonPropertyName("error")] - public string? Error { get; set; } - - /// Machine-readable terminal failure. - [JsonPropertyName("failure")] - public WorkflowRunFailure? Failure { get; set; } + /// The type discriminator. + [JsonPropertyName("type")] + public virtual string Type { get; set; } = string.Empty; +} - /// Pause initiator metadata, or null when the run did not pause. - [JsonPropertyName("pauseInfo")] - public WorkflowPauseInfo? PauseInfo { get; set; } - /// Human-readable terminal reason. - [JsonPropertyName("reason")] - public string? Reason { get; set; } +/// Authentication-info input variant for GitHub-internal HMAC auth, carrying the public GitHub host and HMAC secret. +/// The hmac variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class SettableAuthInfoHmac : SettableAuthInfo +{ + /// + [JsonIgnore] + public override string Type => "hmac"; - /// Prompt-safe preview of the completed result. - [JsonPropertyName("resultPreview")] - public string? ResultPreview { get; set; } + /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("copilotUser")] + public CopilotUserResponse? CopilotUser { get; set; } + + /// HMAC secret used to sign requests. + [JsonPropertyName("hmac")] + public required string Hmac { get; set; } + + /// Authentication host. HMAC auth always targets the public GitHub host. + [JsonPropertyName("host")] + public required string Host { get; set; } } -/// Durable workflow run summary with read-time live overlays. +/// Authentication-info input variant for a token sourced from an environment variable, with host, optional login, token, and env var name. +/// The env variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowRunSummary +public partial class SettableAuthInfoEnv : SettableAuthInfo { - /// Epoch milliseconds when the current active segment started, or null while inactive. - [JsonPropertyName("activeSegmentStartedAt")] - public long? ActiveSegmentStartedAt { get; set; } + /// + [JsonIgnore] + public override string Type => "env"; - /// Approved effective resource ceilings, or null until approved. - [JsonPropertyName("approved")] - public WorkflowDeclaredLimits? Approved { get; set; } + /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("copilotUser")] + public CopilotUserResponse? CopilotUser { get; set; } - /// Whether the durable run state currently passes runtime resume eligibility checks. - [JsonPropertyName("canResume")] - public bool CanResume { get; set; } + /// Name of the environment variable the token was sourced from. + [JsonPropertyName("envVar")] + public required string EnvVar { get; set; } - /// Epoch milliseconds when the run completed, or null while nonterminal. - [JsonPropertyName("completedAt")] - public long? CompletedAt { get; set; } + /// Authentication host (e.g. https://github.com or a GHES host). + [JsonPropertyName("host")] + public required string Host { get; set; } - /// Durable resource consumption. - [JsonPropertyName("consumed")] - public WorkflowRunConsumed Consumed { get => field ??= new(); set; } + /// User login associated with the token. Undefined for server-to-server tokens (those starting with `ghs_`). + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("login")] + public string? Login { get; set; } - /// Epoch milliseconds when the run was created. - [JsonPropertyName("createdAt")] - public long CreatedAt { get; set; } + /// The token value itself. Treat as a secret. + [JsonPropertyName("token")] + public required string Token { get; set; } +} - /// Current phase identity, or null before any phase is entered. - [JsonPropertyName("currentPhase")] - public WorkflowCurrentPhase? CurrentPhase { get; set; } +/// Token authentication accepted by session.gitHubAuth.setCredentials. +/// The token variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class SettableAuthInfoToken : SettableAuthInfo +{ + /// + [JsonIgnore] + public override string Type => "token"; - /// Resource ceilings declared by the workflow. - [JsonPropertyName("declaredLimits")] - public WorkflowDeclaredLimits DeclaredLimits { get => field ??= new(); set; } + /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("copilotUser")] + public CopilotUserResponse? CopilotUser { get; set; } - /// Number of phases declared by the workflow. - [JsonPropertyName("declaredPhaseCount")] - public long DeclaredPhaseCount { get; set; } + /// Authentication host. + [JsonPropertyName("host")] + public required string Host { get; set; } - /// Human-readable workflow description. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; + /// The token value itself. Treat as a secret. + [JsonPropertyName("token")] + public required string Token { get; set; } +} - /// Number of direct workflow agents currently live. - [JsonPropertyName("liveAgentCount")] - public long LiveAgentCount { get; set; } +/// Authentication-info variant for direct Copilot API token auth sourced from environment variables, with public GitHub host. +/// The copilot-api-token variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class SettableAuthInfoCopilotApiToken : SettableAuthInfo +{ + /// + [JsonIgnore] + public override string Type => "copilot-api-token"; - /// Epoch milliseconds when this live-overlay snapshot was observed. - [JsonPropertyName("observedAt")] - public long ObservedAt { get; set; } + /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("copilotUser")] + public CopilotUserResponse? CopilotUser { get; set; } - /// Monotonic durable run revision. - [JsonPropertyName("revision")] - public long Revision { get; set; } + /// Authentication host (always the public GitHub host). + [JsonPropertyName("host")] + public required string Host { get; set; } +} - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; +/// Authentication-info variant for OAuth user auth, with host and login; the token remains in the runtime secret store. +/// The user variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class SettableAuthInfoUser : SettableAuthInfo +{ + /// + [JsonIgnore] + public override string Type => "user"; - /// Epoch milliseconds when execution first started, or null before start. - [JsonPropertyName("startedAt")] - public long? StartedAt { get; set; } + /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("copilotUser")] + public CopilotUserResponse? CopilotUser { get; set; } - /// Current workflow run status. - [JsonPropertyName("status")] - public WorkflowRunStatus Status { get; set; } + /// Authentication host. + [JsonPropertyName("host")] + public required string Host { get; set; } - /// Terminal run outcome, or null while nonterminal. - [JsonPropertyName("terminal")] - public WorkflowRunTerminal? Terminal { get; set; } + /// OAuth user login. + [JsonPropertyName("login")] + public required string Login { get; set; } +} - /// Total direct workflow agents spawned across all attempts. - [JsonPropertyName("totalSpawnedAgentCount")] - public long TotalSpawnedAgentCount { get; set; } +/// An interactive account whose model provider owns its credentials. It carries no GitHub credential. +/// The account variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class SettableAuthInfoAccount : SettableAuthInfo +{ + /// + [JsonIgnore] + public override string Type => "account"; - /// Epoch milliseconds when the durable run was last updated. - [JsonPropertyName("updatedAt")] - public long UpdatedAt { get; set; } + /// Host coordinate owned by the account's model provider. + [JsonPropertyName("host")] + public required string Host { get; set; } - /// Registered workflow name. - [JsonPropertyName("workflowName")] - public string WorkflowName { get; set; } = string.Empty; + /// Login identifying the provider-owned account. + [JsonPropertyName("login")] + public required string Login { get; set; } } -/// A page of workflow runs in durable creation order. +/// Authentication-info input variant for GitHub CLI credentials, carrying host, login, and the `gh auth token` value. +/// The gh-cli variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowListRunsResult +public partial class SettableAuthInfoGhCli : SettableAuthInfo { - /// Whether terminal runs newer than this page exist. - [JsonPropertyName("hasMoreNewer")] - public bool? HasMoreNewer { get; set; } + /// + [JsonIgnore] + public override string Type => "gh-cli"; - /// Newest terminal-run cursor in this page, or null when the terminal window is empty. - [JsonPropertyName("newestSeq")] - public long? NewestSeq { get; set; } + /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("copilotUser")] + public CopilotUserResponse? CopilotUser { get; set; } - /// Oldest terminal-run cursor in this page, or null when the terminal window is empty. - [JsonPropertyName("oldestSeq")] - public long? OldestSeq { get; set; } + /// Authentication host. + [JsonPropertyName("host")] + public required string Host { get; set; } - /// Number of terminal runs older than this page. - [JsonPropertyName("omittedOlder")] - public long? OmittedOlder { get; set; } + /// User login as reported by `gh auth status`. + [JsonPropertyName("login")] + public required string Login { get; set; } - /// Workflow run summaries in durable creation order. - [JsonPropertyName("runs")] - public IList Runs { get => field ??= []; set; } + /// The token returned by `gh auth token`. Treat as a secret. + [JsonPropertyName("token")] + public required string Token { get; set; } } -/// Parameters for paging workflow runs. +/// Authentication-info input variant for API-key authentication to a non-GitHub LLM provider, carrying the secret `apiKey` and host. +/// The api-key variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowListRunsRequest +public partial class SettableAuthInfoApiKey : SettableAuthInfo { - /// Exclusive forward cursor. - [JsonPropertyName("afterSeq")] - public long? AfterSeq { get; set; } + /// + [JsonIgnore] + public override string Type => "api-key"; - /// Exclusive backward cursor. - [JsonPropertyName("beforeSeq")] - public long? BeforeSeq { get; set; } + /// The API key. Treat as a secret. + [JsonPropertyName("apiKey")] + public required string ApiKey { get; set; } - /// Maximum terminal runs to return. Defaults to 200 and is capped at 500. - [JsonPropertyName("limit")] - public int? Limit { get; set; } + /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("copilotUser")] + public CopilotUserResponse? CopilotUser { get; set; } + + /// Authentication host. + [JsonPropertyName("host")] + public required string Host { get; set; } +} + +/// New auth credentials to install on the session. Omit to leave credentials unchanged. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionSetCredentialsParams +{ + /// The new auth credentials to install on the session. When omitted or `undefined`, the call is a no-op and the session's existing credentials are preserved. The runtime installs the supplied value immediately for outbound model/API requests. When the credential carries a raw token (`token`, `env`, or `gh-cli`) but no `copilotUser`, the runtime additionally re-resolves `copilotUser` server-side (best-effort, asynchronously, after the synchronous install) so plan/quota/billing metadata regains fidelity; on resolution failure the verbatim credential remains installed. It does NOT otherwise validate the credential. Several variants carry secret material; treat this method's params as containing secrets at rest and in transit. + [JsonPropertyName("credentials")] + public SettableAuthInfo? Credentials { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Prompt-safe durable identity and live status for a direct workflow agent. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowAgentSummary +internal sealed class SessionGitHubAuthGetCurrentAuthInfoRequest { - /// Accumulated active agent time in milliseconds. - [JsonPropertyName("activeMs")] - public long ActiveMs { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Prompt-safe live activity text. - [JsonPropertyName("activity")] - public string? Activity { get; set; } +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionGitHubAuthGetAllAuthAvailableRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Stable direct-agent identifier. - [JsonPropertyName("agentId")] - public string AgentId { get; set; } = string.Empty; +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionGitHubAuthRefreshCopilotUserRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Registered agent type. - [JsonPropertyName("agentType")] - public string AgentType { get; set; } = string.Empty; +/// Internal GitHub login parameters. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionAuthLoginRequest +{ + /// GitHub host URL. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; - /// Epoch milliseconds when the agent completed. - [JsonPropertyName("completedAt")] - public long? CompletedAt { get; set; } + /// GitHub login. + [JsonPropertyName("login")] + public string Login { get; set; } = string.Empty; - /// Friendly, non-unique name intended for display. - [JsonPropertyName("displayName")] - public string? DisplayName { get; set; } + /// Whether to persist the token after login. + [JsonPropertyName("persist")] + public bool? Persist { get; set; } - /// Friendly, non-unique name intended for display. - [JsonPropertyName("label")] - public string Label { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Phase identifier active when the agent was launched, or null. - [JsonPropertyName("phaseId")] - public string? PhaseId { get; set; } + /// GitHub authentication token. + [JsonPropertyName("token")] + public string Token { get; set; } = string.Empty; +} - /// Model requested when the agent was launched. - [JsonPropertyName("requestedModel")] - public string? RequestedModel { get; set; } +/// Parameters for switching the session's active authentication. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionAuthSwitchRequest +{ + /// Authentication information to activate. + [JsonPropertyName("authInfo")] + public AuthInfo AuthInfo { get => field ??= new(); set; } - /// Concrete model resolved for the agent. - [JsonPropertyName("resolvedModel")] - public string? ResolvedModel { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Owning workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// Optional token paired with the authentication information. + [JsonPropertyName("token")] + public string? Token { get; set; } +} - /// Epoch milliseconds when the agent started. - [JsonPropertyName("startedAt")] - public long? StartedAt { get; set; } +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionGitHubAuthLogoutRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Current durable or live agent status. - [JsonPropertyName("status")] - public string Status { get; set; } = string.Empty; +/// Parameters identifying a GitHub authentication to log out. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionAuthLogoutUserRequest +{ + /// Authentication information to log out. + [JsonPropertyName("authInfo")] + public AuthInfo AuthInfo { get => field ??= new(); set; } - /// Tool-call identifier that launched the agent. - [JsonPropertyName("toolCallId")] - public string ToolCallId { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Durable lifecycle and timing for one workflow phase. +/// Validation error from an authentication attempt. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowPhaseObservation +public sealed class AuthValidationError { - /// Completed active time accumulated by this phase in milliseconds. - [JsonPropertyName("accumulatedActiveMs")] - public long AccumulatedActiveMs { get; set; } - - /// Epoch milliseconds when this phase completed; for a skipped phase, the synthetic skip timestamp (equal to `startedAt`). - [JsonPropertyName("completedAt")] - public long? CompletedAt { get; set; } + /// Optional message returned by GitHub. + [JsonPropertyName("githubMessage")] + public string? GitHubMessage { get; set; } - /// Current live active time for this phase in milliseconds. - [JsonPropertyName("currentActiveMs")] - public long CurrentActiveMs { get; set; } + /// Authentication validation error message. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; +} - /// Optional human-readable phase detail. - [JsonPropertyName("detail")] - public string? Detail { get; set; } +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionGitHubAuthLastAuthErrorsRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Number of times execution entered this phase. - [JsonPropertyName("entryCount")] - public long EntryCount { get; set; } +/// The enumerated collection, keyed by the same selector as the query. +/// Polymorphic base type discriminated by kind. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(AuthEnumerateValueAccounts), "accounts")] +[JsonDerivedType(typeof(AuthEnumerateValueProviders), "providers")] +public partial class AuthEnumerateValue +{ + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// Phase identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; - /// Most recent run attempt that entered this phase, or `0` if the phase has never been entered. - [JsonPropertyName("lastEnteredRunAttempt")] - public long LastEnteredRunAttempt { get; set; } +/// One signed-in account in the roster forest. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class AccountStatus +{ + /// Whether this is the active account. + [JsonPropertyName("active")] + public bool Active { get; set; } - /// Direct agents in this phase that are currently live. - [JsonPropertyName("liveAgentCount")] - public long LiveAgentCount { get; set; } + /// Human-readable credential source used to authenticate this account, such as `gh` or `GITHUB_TOKEN`. + [JsonPropertyName("authSource")] + public string? AuthSource { get; set; } - /// Zero-based declared phase ordinal, or null for an undeclared phase. - [JsonPropertyName("ordinal")] - public long? Ordinal { get; set; } + /// Opaque id of the account this one was derived from (e.g. an EMU account's base Entra identity); absent for a root account. Matches the base identity account's selectionId, forming the derivation edge. + [JsonPropertyName("derivedFrom")] + public string? DerivedFrom { get; set; } - /// Epoch milliseconds when this phase first started; for a skipped phase, the synthetic skip timestamp (equal to `completedAt`). - [JsonPropertyName("startedAt")] - public long? StartedAt { get; set; } + /// Authentication host URL. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; - /// Derived lifecycle state of the phase. - [JsonPropertyName("status")] - public WorkflowPhaseStatus Status { get; set; } + /// The provider kind of this account. + [JsonPropertyName("kind")] + public AccountKind Kind { get; set; } - /// Human-readable phase title. - [JsonPropertyName("title")] - public string Title { get; set; } = string.Empty; + /// Authenticated login/username. + [JsonPropertyName("login")] + public string Login { get; set; } = string.Empty; - /// Total direct agents associated with this phase. - [JsonPropertyName("totalAgentCount")] - public long TotalAgentCount { get; set; } + /// Opaque selection id used to switch to, or log out, this account. + [JsonPropertyName("selectionId")] + public string SelectionId { get; set; } = string.Empty; } -/// One durable workflow progress record. +/// The accounts variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowProgressLine +public partial class AuthEnumerateValueAccounts : AuthEnumerateValue { - /// Resume attempt that emitted this record. - [JsonPropertyName("attempt")] - public long Attempt { get; set; } - - /// Progress record kind. - [JsonPropertyName("kind")] - public WorkflowLogLineKind Kind { get; set; } + /// + [JsonIgnore] + public override string Kind => "accounts"; - /// Phase active when the record was emitted, or null before any phase. - [JsonPropertyName("phaseId")] - public string? PhaseId { get; set; } + /// The signed-in account forest; empty when not logged in. + [JsonPropertyName("items")] + public required IList Items { get; set; } +} - /// Epoch milliseconds when the record was persisted. - [JsonPropertyName("recordedAt")] - public long RecordedAt { get; set; } +/// A provider offered for interactive login. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ProviderDescriptor +{ + /// Whether this provider is currently available to sign in with. + [JsonPropertyName("available")] + public bool Available { get; set; } - /// Global monotonic sequence number within the run. - [JsonPropertyName("seq")] - public long Seq { get; set; } + /// The neutral provider kind. + [JsonPropertyName("kind")] + public LoginProviderKind Kind { get; set; } - /// Prompt-safe progress text. - [JsonPropertyName("text")] - public string Text { get; set; } = string.Empty; + /// Human-readable menu label, owned by the runtime so every consumer renders identical text. + [JsonPropertyName("label")] + public string Label { get; set; } = string.Empty; } -/// A bidirectional page of workflow progress. +/// The providers variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowProgressPage +public partial class AuthEnumerateValueProviders : AuthEnumerateValue { - /// Whether progress records newer than this page exist. - [JsonPropertyName("hasMoreNewer")] - public bool HasMoreNewer { get; set; } - - /// Whether progress records older than this page exist. - [JsonPropertyName("hasMoreOlder")] - public bool HasMoreOlder { get; set; } - - /// Newest sequence number in this page, or null when empty. - [JsonPropertyName("newestSeq")] - public long? NewestSeq { get; set; } - - /// Oldest sequence number in this page, or null when empty. - [JsonPropertyName("oldestSeq")] - public long? OldestSeq { get; set; } - - /// Progress records in sequence order. - [JsonPropertyName("records")] - public IList Records { get => field ??= []; set; } + /// + [JsonIgnore] + public override string Kind => "providers"; - /// Run revision reflected by this page. - [JsonPropertyName("revision")] - public long Revision { get; set; } + /// The providers offered for interactive login. + [JsonPropertyName("items")] + public required IList Items { get; set; } } -/// Full workflow run observability detail. +/// Selects which accounts collection to enumerate. A no-arg selector is the empty-payload variant. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowRunDetail +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(AuthEnumerateQueryAccounts), "accounts")] +[JsonDerivedType(typeof(AuthEnumerateQueryProviders), "providers")] +public partial class AuthEnumerateQuery { - /// Epoch milliseconds when the current active segment started, or null while inactive. - [JsonPropertyName("activeSegmentStartedAt")] - public long? ActiveSegmentStartedAt { get; set; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// Durable identities and live statuses for direct workflow agents. - [JsonPropertyName("agents")] - public IList Agents { get => field ??= []; set; } - /// Approved effective resource ceilings, or null until approved. - [JsonPropertyName("approved")] - public WorkflowDeclaredLimits? Approved { get; set; } +/// The accounts variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class AuthEnumerateQueryAccounts : AuthEnumerateQuery +{ + /// + [JsonIgnore] + public override string Kind => "accounts"; +} - /// Whether the durable run state currently passes runtime resume eligibility checks. - [JsonPropertyName("canResume")] - public bool CanResume { get; set; } +/// The providers variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class AuthEnumerateQueryProviders : AuthEnumerateQuery +{ + /// + [JsonIgnore] + public override string Kind => "providers"; - /// Epoch milliseconds when the run completed, or null while nonterminal. - [JsonPropertyName("completedAt")] - public long? CompletedAt { get; set; } + /// Whether an interactive Entra broker is available on the host; gates Entra availability in the returned list. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("brokerAvailable")] + public bool? BrokerAvailable { get; set; } +} - /// Durable resource consumption. - [JsonPropertyName("consumed")] - public WorkflowRunConsumed Consumed { get => field ??= new(); set; } +/// Enumerate request carrying the typed collection query. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class AccountsEnumerateRequest +{ + /// Which typed accounts collection to enumerate. + [JsonPropertyName("query")] + public AuthEnumerateQuery Query { get => field ??= new(); set; } - /// Epoch milliseconds when the run was created. - [JsonPropertyName("createdAt")] - public long CreatedAt { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Current phase identity, or null before any phase is entered. - [JsonPropertyName("currentPhase")] - public WorkflowCurrentPhase? CurrentPhase { get; set; } +/// The read result, keyed by the same selector as the query. +/// Polymorphic base type discriminated by kind. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(AuthReadValueActiveAccount), "activeAccount")] +[JsonDerivedType(typeof(AuthReadValueStatus), "status")] +[JsonDerivedType(typeof(AuthReadValueLastErrors), "lastErrors")] +public partial class AuthReadValue +{ + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// Resource ceilings declared by the workflow. - [JsonPropertyName("declaredLimits")] - public WorkflowDeclaredLimits DeclaredLimits { get => field ??= new(); set; } - /// Number of phases declared by the workflow. - [JsonPropertyName("declaredPhaseCount")] - public long DeclaredPhaseCount { get; set; } +/// The activeAccount variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class AuthReadValueActiveAccount : AuthReadValue +{ + /// + [JsonIgnore] + public override string Kind => "activeAccount"; - /// Human-readable workflow description. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; + /// The active account, or absent when not logged in. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("account")] + public AccountStatus? Account { get; set; } - /// Number of direct workflow agents currently live. - [JsonPropertyName("liveAgentCount")] - public long LiveAgentCount { get; set; } + /// Credential-free identity metadata for the active account, including resolved Copilot user information when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("authInfo")] + public AuthIdentity? AuthInfo { get; set; } +} - /// Epoch milliseconds when this live-overlay snapshot was observed. - [JsonPropertyName("observedAt")] - public long ObservedAt { get; set; } +/// Neutral authentication status summary. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class AuthStatusDto +{ + /// Number of signed-in accounts in the roster. + [JsonPropertyName("accountCount")] + public long AccountCount { get; set; } - /// Lifecycle and timing observations for each workflow phase. - [JsonPropertyName("phases")] - public IList Phases { get => field ??= []; set; } + /// Active account host, if authenticated. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("activeHost")] + public string? ActiveHost { get; set; } - /// Bidirectional page of durable workflow progress. - [JsonPropertyName("progress")] - public WorkflowProgressPage Progress { get => field ??= new(); set; } + /// Active account login, if authenticated. + [JsonPropertyName("activeLogin")] + public string? ActiveLogin { get; set; } - /// Monotonic durable run revision. - [JsonPropertyName("revision")] - public long Revision { get; set; } + /// Copilot plan tier of the active account, if known. + [JsonPropertyName("copilotPlan")] + public string? CopilotPlan { get; set; } - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// Whether the session has resolved authentication. + [JsonPropertyName("isAuthenticated")] + public bool IsAuthenticated { get; set; } +} - /// Epoch milliseconds when execution first started, or null before start. - [JsonPropertyName("startedAt")] - public long? StartedAt { get; set; } +/// The status variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class AuthReadValueStatus : AuthReadValue +{ + /// + [JsonIgnore] + public override string Kind => "status"; - /// Current workflow run status. + /// The neutral authentication status summary. [JsonPropertyName("status")] - public WorkflowRunStatus Status { get; set; } - - /// Terminal run outcome, or null while nonterminal. - [JsonPropertyName("terminal")] - public WorkflowRunTerminal? Terminal { get; set; } - - /// Total direct workflow agents spawned across all attempts. - [JsonPropertyName("totalSpawnedAgentCount")] - public long TotalSpawnedAgentCount { get; set; } - - /// Epoch milliseconds when the durable run was last updated. - [JsonPropertyName("updatedAt")] - public long UpdatedAt { get; set; } - - /// Registered workflow name. - [JsonPropertyName("workflowName")] - public string WorkflowName { get; set; } = string.Empty; + public required AuthStatusDto Status { get; set; } } -/// Parameters for paging workflow progress. +/// The lastErrors variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowGetRunProgressRequest +public partial class AuthReadValueLastErrors : AuthReadValue { - /// Exclusive forward cursor. - [JsonPropertyName("afterSeq")] - public long? AfterSeq { get; set; } - - /// Exclusive backward cursor. - [JsonPropertyName("beforeSeq")] - public long? BeforeSeq { get; set; } + /// + [JsonIgnore] + public override string Kind => "lastErrors"; - /// Maximum records to return. Defaults to 200 and is capped at 500. - [JsonPropertyName("limit")] - public int? Limit { get; set; } + /// Validation errors from the most recent authentication attempt. + [JsonPropertyName("errors")] + public required IList Errors { get; set; } +} - /// Optional phase identifier used to scope records and cursors. - [JsonPropertyName("phaseId")] - public string? PhaseId { get; set; } +/// Selects which typed accounts datum to read. +/// Polymorphic base type discriminated by kind. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(AuthReadQueryActiveAccount), "activeAccount")] +[JsonDerivedType(typeof(AuthReadQueryStatus), "status")] +[JsonDerivedType(typeof(AuthReadQueryLastErrors), "lastErrors")] +public partial class AuthReadQuery +{ + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; +/// The activeAccount variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class AuthReadQueryActiveAccount : AuthReadQuery +{ + /// + [JsonIgnore] + public override string Kind => "activeAccount"; } -/// Parameters for cancelling a workflow run. +/// The status variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowCancelRequest +public partial class AuthReadQueryStatus : AuthReadQuery { - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "status"; +} - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; +/// The lastErrors variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class AuthReadQueryLastErrors : AuthReadQuery +{ + /// + [JsonIgnore] + public override string Kind => "lastErrors"; } -/// Parameters for pausing a running workflow. +/// Read request carrying the typed datum query. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowPauseRequest +internal sealed class AccountsGetRequest { - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// Which typed accounts datum to read. + [JsonPropertyName("query")] + public AuthReadQuery Query { get => field ??= new(); set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// RPC data type for SessionWorkflowPauseAtCheckpoint operations. +/// Result of a non-interactive accounts mutation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionWorkflowPauseAtCheckpointResult +public sealed class AuthWriteResult { - /// Whether this execution attempt must pause or may continue. - [JsonPropertyName("action")] - public WorkflowPauseCheckpointAction Action { get; set; } + /// For a logout, whether other signed-in accounts remain. + [JsonPropertyName("moreUsers")] + public bool? MoreUsers { get; set; } + + /// Whether the mutation was applied. + [JsonPropertyName("ok")] + public bool Ok { get; set; } } -/// Parameters for an owned durable pause checkpoint. +/// One non-interactive accounts mutation command (the selector is fused with its typed args). +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowPauseCheckpointRequest +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(AuthWriteSwitchActive), "switchActive")] +[JsonDerivedType(typeof(AuthWriteLogout), "logout")] +[JsonDerivedType(typeof(AuthWriteSetCredentials), "setCredentials")] +public partial class AuthWrite { - /// Opaque token identifying the execution attempt that reached the checkpoint. - [JsonPropertyName("executionToken")] - public string ExecutionToken { get; set; } = string.Empty; + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// Stable author-defined checkpoint key. - [JsonPropertyName("key")] - public string Key { get; set; } = string.Empty; - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; +/// The switchActive variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class AuthWriteSwitchActive : AuthWrite +{ + /// + [JsonIgnore] + public override string Kind => "switchActive"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Opaque selection id of the account to make active. + [JsonPropertyName("selectionId")] + public required string SelectionId { get; set; } } -/// Acknowledgement that a workflow request was accepted. +/// The logout variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowAckResult +public partial class AuthWriteLogout : AuthWrite { + /// + [JsonIgnore] + public override string Kind => "logout"; + + /// Opaque selection id of the account to log out; absent logs out the active account. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("selectionId")] + public string? SelectionId { get; set; } } -/// One ordered workflow progress line. +/// The setCredentials variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowLogLine +public partial class AuthWriteSetCredentials : AuthWrite { - /// Progress line kind. - [JsonPropertyName("kind")] - public WorkflowLogLineKind Kind { get; set; } + /// + [JsonIgnore] + public override string Kind => "setCredentials"; - /// Monotonic sequence number within the workflow run. - [JsonPropertyName("seq")] - public long Seq { get; set; } + /// Authentication host URL. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("host")] + public required string Host { get; set; } - /// Progress text. - [JsonPropertyName("text")] - public string Text { get; set; } = string.Empty; + /// Login/username for the credential. + [JsonPropertyName("login")] + public required string Login { get; set; } + + /// GitHub authentication token to install. + [JsonPropertyName("token")] + public required string Token { get; set; } } -/// Parameters for recording workflow progress. +/// Mutation request carrying the typed write command. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowLogRequest +internal sealed class AccountsSetRequest { - /// Opaque token identifying the current workflow execution attempt. - [JsonPropertyName("executionToken")] - public string ExecutionToken { get; set; } = string.Empty; - - /// Ordered progress lines to append. - [JsonPropertyName("lines")] - public IList Lines { get => field ??= []; set; } - - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// The non-interactive mutation command to apply. + [JsonPropertyName("command")] + public AuthWrite Command { get => field ??= new(); set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of one workflow-scoped subagent call. +/// One step in an interactive login flow. The consumer acts on the step and calls advance to proceed. Browser-open is encoded as two distinct steps by design: `open-url` is CONSUMER-driven (the provider surfaces the authorize URL and the consumer opens it — github.com/GHEC web), while `needs-interaction` is PROVIDER-driven (the provider opens the browser or broker UI itself and does not surface a URL — Entra). +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowAgentResult +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(AuthLoginStepOpenUrl), "open-url")] +[JsonDerivedType(typeof(AuthLoginStepInputRequired), "input-required")] +[JsonDerivedType(typeof(AuthLoginStepAwaiting), "awaiting")] +[JsonDerivedType(typeof(AuthLoginStepNeedsInteraction), "needs-interaction")] +[JsonDerivedType(typeof(AuthLoginStepCompleted), "completed")] +[JsonDerivedType(typeof(AuthLoginStepError), "error")] +public partial class AuthLoginStep { - /// Agent result, omitted when the agent produced no result. - [JsonPropertyName("result")] - public JsonElement? Result { get; set; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// Options for one workflow-scoped subagent call. + +/// The open-url variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowAgentOptions +public partial class AuthLoginStepOpenUrl : AuthLoginStep { - /// Optional built-in or custom agent name whose definition configures the subagent. - [JsonPropertyName("agent")] - public string? Agent { get; set; } - - /// Optional context tier override for the subagent. - [JsonPropertyName("contextTier")] - public ContextTier? ContextTier { get; set; } - - /// Optional label distinguishing otherwise identical memoized agent calls. - [JsonPropertyName("label")] - public string? Label { get; set; } - - /// Optional model identifier for the subagent. - [JsonPropertyName("model")] - public string? Model { get; set; } - - /// Optional reasoning effort override for the subagent. - [JsonPropertyName("reasoningEffort")] - public string? ReasoningEffort { get; set; } + /// + [JsonIgnore] + public override string Kind => "open-url"; - /// Optional JSON Schema for structured agent output. - [JsonPropertyName("schema")] - public JsonElement? Schema { get; set; } + /// Authorize URL the consumer should open in a browser (consumer-driven browser-open). + [JsonPropertyName("url")] + public required string Url { get; set; } } -/// Parameters for one workflow-scoped subagent call. +/// The input-required variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowAgentRequest +public partial class AuthLoginStepInputRequired : AuthLoginStep { - /// Opaque token identifying the current workflow execution attempt. - [JsonPropertyName("executionToken")] - public string ExecutionToken { get; set; } = string.Empty; - - /// Subagent execution options. - [JsonPropertyName("opts")] - public WorkflowAgentOptions Opts { get => field ??= new(); set; } + /// + [JsonIgnore] + public override string Kind => "input-required"; - /// Prompt to send to the subagent. + /// Prompt for the value the provider needs; the consumer supplies it as advance input (e.g. a GitHub Enterprise Cloud host, *.ghe.com). [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; - - /// Workflow run identifier that owns the subagent. - [JsonPropertyName("workflowRunId")] - public string WorkflowRunId { get; set; } = string.Empty; + public required string Prompt { get; set; } } -/// Result of reading a workflow journal entry. +/// The awaiting variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowJournalGetResult +public partial class AuthLoginStepAwaiting : AuthLoginStep { - /// Whether the journal contained the requested key. - [JsonPropertyName("hit")] - public bool Hit { get; set; } + /// + [JsonIgnore] + public override string Kind => "awaiting"; +} - /// Cached JSON result. The hit field distinguishes a cached JSON null from a miss. - [JsonPropertyName("resultJson")] - public JsonElement? ResultJson { get; set; } +/// The needs-interaction variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class AuthLoginStepNeedsInteraction : AuthLoginStep +{ + /// + [JsonIgnore] + public override string Kind => "needs-interaction"; } -/// Parameters for reading a workflow journal entry. +/// A credential-free account choice after sign-in. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowJournalGetRequest +public sealed class AuthLoginAccount { - /// Opaque token identifying the current workflow execution attempt. - [JsonPropertyName("executionToken")] - public string ExecutionToken { get; set; } = string.Empty; + /// Host coordinate owned by the selected account's provider. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; - /// Namespaced journal key. - [JsonPropertyName("key")] - public string Key { get; set; } = string.Empty; + /// Provider kind that owns this account choice. + [JsonPropertyName("kind")] + public AccountKind Kind { get; set; } - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// Human-readable login for the account choice. + [JsonPropertyName("login")] + public string Login { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Opaque identifier supplied to the next login step to select this account. + [JsonPropertyName("selectionId")] + public string SelectionId { get; set; } = string.Empty; } -/// Parameters for storing a workflow journal entry. +/// Result of an interactive login flow. Pending consent or account selection is not terminal. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowJournalPutRequest +public sealed class AuthLoginResultDto { - /// Opaque token identifying the current workflow execution attempt. - [JsonPropertyName("executionToken")] - public string ExecutionToken { get; set; } = string.Empty; + /// Available accounts when sign-in is awaiting account selection, ordered with Microsoft 365 first. + [JsonPropertyName("accounts")] + public IList? Accounts { get; set; } - /// Namespaced journal key. - [JsonPropertyName("key")] - public string Key { get; set; } = string.Empty; + /// Host that was signed in, when completed. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("host")] + public string? Host { get; set; } - /// JSON result to memoize. - [JsonPropertyName("resultJson")] - public JsonElement ResultJson { get; set; } + /// Login that was signed in, when completed. + [JsonPropertyName("login")] + public string? Login { get; set; } - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// Current disposition of the login, including pending user decisions. + [JsonPropertyName("status")] + public AuthLoginResultStatus Status { get; set; } +} - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; +/// The completed variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class AuthLoginStepCompleted : AuthLoginStep +{ + /// + [JsonIgnore] + public override string Kind => "completed"; + + /// Login result. When status is needs-plaintext-consent or needs-account-selection, advance with the user's decision to continue. + [JsonPropertyName("result")] + public required AuthLoginResultDto Result { get; set; } } -/// The session's authoritative model snapshot. Auto preference fields are configuration for the virtual `auto` model and do not change the selected model identifier. The context tier reflects `Session.getContextTier()`, restored from the session journal on resume. +/// The error variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CurrentModel +public partial class AuthLoginStepError : AuthLoginStep { - /// Auto preference currently claimed by an in-progress activation. Null means the activation is returning to provider-default routing. - [JsonPropertyName("activatingAutoTier")] - public AutoTier? ActivatingAutoTier { get; set; } + /// + [JsonIgnore] + public override string Kind => "error"; - /// Auto preference currently committed for the session. This can remain available while another model is selected so a later switch to `auto` can reuse it. - [JsonPropertyName("autoTier")] - public AutoTier? AutoTier { get; set; } + /// Human-readable failure message. + [JsonPropertyName("message")] + public required string Message { get; set; } +} - /// Context tier for models that support multiple context-window sizes. - [JsonPropertyName("contextTier")] - public ContextTier? ContextTier { get; set; } +/// A started login flow: its opaque id and first step. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class AuthLoginBegun +{ + /// Opaque flow id used to advance or cancel this login. + [JsonPropertyName("flowId")] + public string FlowId { get; set; } = string.Empty; - /// Currently active model identifier. - [JsonPropertyName("modelId")] - public string? ModelId { get; set; } + /// The first step of the flow. + [JsonPropertyName("step")] + public AuthLoginStep Step { get => field ??= new(); set; } +} - /// Latest unclaimed Auto preference waiting for a future user turn. Null means the pending request is returning to provider-default routing. - [JsonPropertyName("pendingAutoTier")] - public AutoTier? PendingAutoTier { get; set; } +/// Begin an interactive login flow for a provider kind. Dispatch is kind-only. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class AuthLoginBeginRequest +{ + /// The provider kind to sign in with. + [JsonPropertyName("kind")] + public LoginProviderKind Kind { get; set; } - /// Captured base model to restore when leaving plan mode. Omitted outside plan mode or when no plan override has captured a base model. Persistent agent model requirements apply to this model rather than the temporary plan model. - [JsonPropertyName("planBaseModelId")] - public string? PlanBaseModelId { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Provider of planBaseModelId, when that saved selection is provider-qualified. - [JsonPropertyName("planBaseProviderId")] - public string? PlanBaseProviderId { get; set; } +/// Advance an in-flight login flow, optionally fulfilling an input-required step. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class AuthLoginAdvanceRequest +{ + /// Opaque flow id from begin. + [JsonPropertyName("flowId")] + public string FlowId { get; set; } = string.Empty; - /// Provider selected for this model. Together with modelId, identifies a catalog entry even when providers serve the same model. Omitted for unattributed legacy selections. - [JsonPropertyName("providerId")] - public string? ProviderId { get; set; } + /// Neutral input fulfilling a preceding input-required step (e.g. a GHEC host); ignored otherwise. + [JsonPropertyName("input")] + public string? Input { get; set; } - /// Reasoning effort level currently applied to the active model, when one is set. Reads `Session.getReasoningEffort()` synchronously after `getSelectedModel()` resolves so the two values are reported as a snapshot. - [JsonPropertyName("reasoningEffort")] - public string? ReasoningEffort { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Identifies the target session. +/// Cancel an in-flight login flow. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionModelGetCurrentRequest +internal sealed class AuthLoginCancelRequest { + /// Opaque flow id from begin. + [JsonPropertyName("flowId")] + public string FlowId { get; set; } = string.Empty; + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// RPC data type for ModelSwitchConfirmation operations. +/// A file included in the session debug bundle. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelSwitchConfirmation +public sealed class DebugCollectLogsCollectedEntry { - /// Current conversation token count before switching models. - [JsonPropertyName("currentTokens")] - public double CurrentTokens { get; set; } + /// Relative path of the file in the staged bundle/archive. + [JsonPropertyName("bundlePath")] + public string BundlePath { get; set; } = string.Empty; - /// Target model token limit used by the compaction preflight. - [JsonPropertyName("targetLimit")] - public double TargetLimit { get; set; } + /// Redacted output size in bytes. + [JsonPropertyName("sizeBytes")] + public long SizeBytes { get; set; } - /// Display name of the model that requires compaction confirmation. - [JsonPropertyName("targetModelDisplayName")] - public string TargetModelDisplayName { get; set; } = string.Empty; + /// Source category for this entry. + [JsonPropertyName("source")] + public DebugCollectLogsSource Source { get; set; } } -/// The model identifier active on the session after the switch. +/// An optional debug bundle entry that could not be included. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelSwitchToResult +public sealed class DebugCollectLogsSkippedEntry { - /// Compaction confirmation projection when status is confirmation_required. - [JsonPropertyName("confirmation")] - public ModelSwitchConfirmation? Confirmation { get; set; } + /// Relative path requested for this bundle entry. + [JsonPropertyName("bundlePath")] + public string BundlePath { get; set; } = string.Empty; - /// True when the switch was deferred (enqueued as a cancellable `/model` command) because a turn was active or another model change was already queued, rather than applied immediately. When true, the session's live model is unchanged until the queued change drains. - [JsonPropertyName("deferred")] - public bool? Deferred { get; set; } + /// Server-local source path that could not be read. + [JsonPropertyName("path")] + public string? Path { get; set; } - /// Deprecation warnings associated with the selected model or options. - [JsonPropertyName("deprecationWarnings")] - public IList? DeprecationWarnings { get; set; } + /// Reason the entry was skipped. + [JsonPropertyName("reason")] + public string Reason { get; set; } = string.Empty; +} - /// User-facing outcome message for the model switch. - [JsonPropertyName("message")] - public string? Message { get; set; } +/// Result of collecting a session debug bundle. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class DebugCollectLogsResult +{ + /// Files included in the bundle. + [JsonPropertyName("entries")] + public IList Entries { get => field ??= []; set; } - /// Currently active model identifier after the switch. - [JsonPropertyName("modelId")] - public string? ModelId { get; set; } + /// Destination kind that was written. + [JsonPropertyName("kind")] + public DebugCollectLogsResultKind Kind { get; set; } - /// Authoritative model and Auto preference state after an immediate switch. For deferred switches this remains the current state until the queued change drains. - [JsonPropertyName("modelState")] - public CurrentModel? ModelState { get; set; } + /// Actual archive path or staging directory path written. This may differ from the requested path when no-overwrite suffixing or fallback-to-temp-directory was needed. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; - /// Persistence failure encountered after applying the model switch. - [JsonPropertyName("persistenceError")] - public string? PersistenceError { get; set; } + /// Optional files or directories that could not be included. + [JsonPropertyName("skippedEntries")] + public IList? SkippedEntries { get; set; } +} - /// Stable queue item identifier when this request was enqueued. Remains present if the item drains before the response is returned. - [JsonPropertyName("queueId")] - public string? QueueId { get; set; } +/// A caller-provided server-local file or directory to include in the debug bundle. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class DebugCollectLogsEntry +{ + /// Relative path to use inside the staged bundle/archive. + [JsonPropertyName("bundlePath")] + public string BundlePath { get; set; } = string.Empty; - /// Lifecycle result for the requested switch. - [JsonPropertyName("status")] - public string? Status { get; set; } + /// Kind of source path to include. + [JsonPropertyName("kind")] + public DebugCollectLogsEntryKind Kind { get; set; } - /// User-facing warning produced while applying the model switch. - [JsonPropertyName("warning")] - public string? Warning { get; set; } + /// Server-local source path to read. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + + /// How text content from this entry should be redacted. Defaults to plain-text. With none, no redaction is applied; the caller must ensure any necessary redaction is performed before this call. + [JsonPropertyName("redaction")] + public DebugCollectLogsRedaction? Redaction { get; set; } + + /// When true, collection fails if this entry cannot be read. Defaults to false, which records the entry in `skippedEntries`. + [JsonPropertyName("required")] + public bool? Required { get; set; } } -/// Environment variables consulted while resolving model-picker settings. -public sealed class ModelPickerSettingsContextEnvironment +/// Destination for the session debug bundle. +/// Polymorphic base type discriminated by kind. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(DebugCollectLogsDestinationArchive), "archive")] +[JsonDerivedType(typeof(DebugCollectLogsDestinationDirectory), "directory")] +public partial class DebugCollectLogsDestination { + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// Filesystem and environment context used to resolve model-picker settings. + +/// The archive variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelPickerSettingsContext +public partial class DebugCollectLogsDestinationArchive : DebugCollectLogsDestination { - /// Optional Copilot configuration directory containing persisted settings. - [JsonPropertyName("configDir")] - public string? ConfigDir { get; set; } + /// + [JsonIgnore] + public override string Kind => "archive"; - /// Environment variables consulted while resolving model-picker settings. - [JsonPropertyName("environment")] - public ModelPickerSettingsContextEnvironment Environment { get => field ??= new(); set; } + /// When true, create the archive atomically without overwriting an existing file by appending ` (N)` before the extension as needed. Defaults to false. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("noOverwrite")] + public bool? NoOverwrite { get; set; } - /// User home directory used when resolving persisted settings. - [JsonPropertyName("homeDirectory")] - public string HomeDirectory { get; set; } = string.Empty; + /// Absolute or server-relative path for the .tgz archive to create. + [JsonPropertyName("outputPath")] + public required string OutputPath { get; set; } } -/// RPC data type for ModelPickerPersistence operations. +/// The directory variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelPickerPersistenceRequest +public partial class DebugCollectLogsDestinationDirectory : DebugCollectLogsDestination { - /// Whether context tier was explicitly selected and should be persisted. - [JsonPropertyName("contextTierExplicit")] - public bool? ContextTierExplicit { get; set; } - - /// Whether reasoning effort was explicitly selected and should be persisted. - [JsonPropertyName("reasoningEffortExplicit")] - public bool? ReasoningEffortExplicit { get; set; } + /// + [JsonIgnore] + public override string Kind => "directory"; - /// Filesystem and environment context used to resolve settings persistence. - [JsonPropertyName("settingsContext")] - public ModelPickerSettingsContext SettingsContext { get => field ??= new(); set; } + /// Directory where files should be staged. The directory is created if needed. + [JsonPropertyName("outputDirectory")] + public required string OutputDirectory { get; set; } } -/// Target model identifier and optional reasoning effort, summary, capability overrides, and context tier. +/// Built-in session diagnostics to include in the bundle. Omitted fields default to true. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelSwitchToRequest +public sealed class DebugCollectLogsInclude { - /// Optional Auto routing preference to stage atomically with selecting `auto`. Pass null to return to provider-default Auto routing. This field is rejected when `modelId` is not `auto`. - [JsonPropertyName("autoTier")] - public AutoTier? AutoTier { get; set; } - - /// Explicit response to a model-switch compaction preflight. Omit to request a confirmation projection when compaction is necessary. - [JsonPropertyName("compactionDecision")] - public string? CompactionDecision { get; set; } - - /// Explicit context tier for the selected model. `"default"` / `"long_context"` apply the requested tier; omit this field to use normal model behavior with no explicit tier. - [JsonPropertyName("contextTier")] - public ContextTier? ContextTier { get; set; } - - /// When true, defer this switch (enqueue it) if another model change is already queued, even when no turn is active — so it drains last (FIFO) and wins over the already-queued change. Intended for genuine user-initiated model selections; internal restore/reapply switches omit it and apply immediately when no turn is active. When no other model change is queued this has no effect (a switch still applies immediately unless a turn is active). - [JsonPropertyName("deferIfModelChangeQueued")] - public bool? DeferIfModelChangeQueued { get; set; } - - /// Override individual model capabilities resolved by the runtime. - [JsonPropertyName("modelCapabilities")] - public ModelCapabilitiesOverride? ModelCapabilities { get; set; } - - /// Settings scope used when persisting the selected model. - [JsonPropertyName("modelChangeScope")] - public string? ModelChangeScope { get; set; } + /// Server-local path to the current process log. When set, it is included as `process.log` and its directory is searched for prior logs from the same session. + [JsonPropertyName("currentProcessLogPath")] + public string? CurrentProcessLogPath { get; set; } - /// Model id to switch to, as returned by `list`. Include providerId to select an exact catalog entry when providers share the id. Without providerId, a bare id retains incumbent selection behavior; a registry-qualified id (`provider/id`, e.g. `acme/claude-sonnet`) targets a BYOK model. - [JsonPropertyName("modelId")] - public required string ModelId { get; set; } + /// Include the session event log (`events.jsonl`). Defaults to true. + [JsonPropertyName("events")] + public bool? Events { get; set; } - /// Optional settings context and explicit-override flags used to persist a picker selection. - [JsonPropertyName("pickerPersistence")] - public ModelPickerPersistenceRequest? PickerPersistence { get; set; } + /// Server-local path to the session's events.jsonl file. Internal callers normally omit this and let the runtime derive it from the session. + [JsonPropertyName("eventsPath")] + public string? EventsPath { get; set; } - /// Provider id from the selected list entry's provider reference. Selects this exact provider/model pair; an unavailable pair fails rather than using another provider. Omit for deterministic legacy bare-model selection. - [JsonPropertyName("providerId")] - public string? ProviderId { get; set; } + /// Maximum number of previous process logs to include. Defaults to 5. + [JsonPropertyName("previousProcessLogLimit")] + public long? PreviousProcessLogLimit { get; set; } - /// Reasoning effort level to use for the model. CAPI values are model-defined and validated against the selected model; BYOK providers may define additional values. "none" disables reasoning. Pass null to clear any session effort override and fall back to the model's default. When omitted, the session's current effort is kept. - [JsonPropertyName("reasoningEffort")] - public string? ReasoningEffort { get; set; } + /// Server-local process log directory to search when `currentProcessLogPath` is unavailable, useful for collecting logs for inactive sessions. + [JsonPropertyName("processLogDirectory")] + public string? ProcessLogDirectory { get; set; } - /// Reasoning summary mode to request for supported model clients. - [JsonPropertyName("reasoningSummary")] - public ReasoningSummary? ReasoningSummary { get; set; } + /// Include process logs for the session. Defaults to true. + [JsonPropertyName("processLogs")] + public bool? ProcessLogs { get; set; } - /// Optional repository settings scope to persist after the switch commits. - [JsonPropertyName("repoScope")] - public string? RepoScope { get; set; } + /// Include interactive shell logs written under the session's `shell-logs` directory. Defaults to true. + [JsonPropertyName("shellLogs")] + public bool? ShellLogs { get; set; } +} - /// Require the target to be currently available and enabled before applying the switch. - [JsonPropertyName("requireAvailable")] - public bool? RequireAvailable { get; set; } +/// Options for collecting a session debug bundle with configurable redaction. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class DebugCollectLogsRequest +{ + /// Caller-provided server-local files or directories to include in addition to the runtime's built-in session diagnostics. This lets host applications add their own diagnostics without changing the API shape. + [JsonPropertyName("additionalEntries")] + public IList? AdditionalEntries { get; set; } - /// When true, evaluate context-window compaction policy before applying the switch. - [JsonPropertyName("runCompactionPreflight")] - public bool? RunCompactionPreflight { get; set; } + /// Where the bundle should be written. Use `archive` to produce a .tgz, or `directory` to stage files for caller-managed upload/post-processing. + [JsonPropertyName("destination")] + public DebugCollectLogsDestination Destination { get => field ??= new(); set; } - /// Origin to record on the effective `session.model_change` event for trusted in-process calls. Transport SDK calls are always recorded as `sdk`, regardless of this value. - [JsonPropertyName("source")] - public ModelChangeSource? Source { get; set; } + /// Which built-in session diagnostics to include. Omitted fields default to true. + [JsonPropertyName("include")] + public DebugCollectLogsInclude? Include { get; set; } - /// Output verbosity level to request for supported models. - [JsonPropertyName("verbosity")] - public Verbosity? Verbosity { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Target model identifier and optional reasoning effort, summary, capability overrides, and context tier. +/// Canvas action that the agent or host can invoke. To discover the input schema for a particular action, call the list_canvas_capabilities tool. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelSwitchToRequestWithSession +public sealed class CanvasAction { - /// Optional Auto routing preference to stage atomically with selecting `auto`. Pass null to return to provider-default Auto routing. This field is rejected when `modelId` is not `auto`. - [JsonPropertyName("autoTier")] - public AutoTier? AutoTier { get; set; } - - /// Explicit response to a model-switch compaction preflight. Omit to request a confirmation projection when compaction is necessary. - [JsonPropertyName("compactionDecision")] - public string? CompactionDecision { get; set; } - - /// Explicit context tier for the selected model. `"default"` / `"long_context"` apply the requested tier; omit this field to use normal model behavior with no explicit tier. - [JsonPropertyName("contextTier")] - public ContextTier? ContextTier { get; set; } + /// Description of the action. + [JsonPropertyName("description")] + public string? Description { get; set; } - /// When true, defer this switch (enqueue it) if another model change is already queued, even when no turn is active — so it drains last (FIFO) and wins over the already-queued change. Intended for genuine user-initiated model selections; internal restore/reapply switches omit it and apply immediately when no turn is active. When no other model change is queued this has no effect (a switch still applies immediately unless a turn is active). - [JsonPropertyName("deferIfModelChangeQueued")] - public bool? DeferIfModelChangeQueued { get; set; } + /// JSON Schema for the action input. + [JsonPropertyName("inputSchema")] + public JsonElement? InputSchema { get; set; } - /// Override individual model capabilities resolved by the runtime. - [JsonPropertyName("modelCapabilities")] - public ModelCapabilitiesOverride? ModelCapabilities { get; set; } + /// Action name exposed by the canvas provider. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; +} - /// Settings scope used when persisting the selected model. - [JsonPropertyName("modelChangeScope")] - public string? ModelChangeScope { get; set; } +/// Canvas available in the current session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class DiscoveredCanvas +{ + /// Actions the agent or host may invoke on an open instance. + [JsonPropertyName("actions")] + public IList? Actions { get; set; } - /// Model id to switch to, as returned by `list`. Include providerId to select an exact catalog entry when providers share the id. Without providerId, a bare id retains incumbent selection behavior; a registry-qualified id (`provider/id`, e.g. `acme/claude-sonnet`) targets a BYOK model. - [JsonPropertyName("modelId")] - public string ModelId { get; set; } = string.Empty; + /// Provider-local canvas identifier. + [JsonPropertyName("canvasId")] + public string CanvasId { get; set; } = string.Empty; - /// Optional settings context and explicit-override flags used to persist a picker selection. - [JsonPropertyName("pickerPersistence")] - public ModelPickerPersistenceRequest? PickerPersistence { get; set; } + /// Short, single-sentence description shown to the agent in canvas catalogs. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; - /// Provider id from the selected list entry's provider reference. Selects this exact provider/model pair; an unavailable pair fails rather than using another provider. Omit for deterministic legacy bare-model selection. - [JsonPropertyName("providerId")] - public string? ProviderId { get; set; } + /// Human-readable canvas name. + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } = string.Empty; - /// Reasoning effort level to use for the model. CAPI values are model-defined and validated against the selected model; BYOK providers may define additional values. "none" disables reasoning. Pass null to clear any session effort override and fall back to the model's default. When omitted, the session's current effort is kept. - [JsonPropertyName("reasoningEffort")] - public string? ReasoningEffort { get; set; } + /// Owning provider identifier. + [JsonPropertyName("extensionId")] + public string ExtensionId { get; set; } = string.Empty; - /// Reasoning summary mode to request for supported model clients. - [JsonPropertyName("reasoningSummary")] - public ReasoningSummary? ReasoningSummary { get; set; } + /// Owning extension display name, when available. + [JsonPropertyName("extensionName")] + public string? ExtensionName { get; set; } - /// Optional repository settings scope to persist after the switch commits. - [JsonPropertyName("repoScope")] - public string? RepoScope { get; set; } + /// Host-local PNG path for the canvas icon, when supplied. + [JsonPropertyName("icon")] + public string? Icon { get; set; } - /// Require the target to be currently available and enabled before applying the switch. - [JsonPropertyName("requireAvailable")] - public bool? RequireAvailable { get; set; } + /// JSON Schema for canvas open input. + [JsonPropertyName("inputSchema")] + public JsonElement? InputSchema { get; set; } +} - /// When true, evaluate context-window compaction policy before applying the switch. - [JsonPropertyName("runCompactionPreflight")] - public bool? RunCompactionPreflight { get; set; } +/// Declared canvases available in this session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class CanvasList +{ + /// Declared canvases available in this session. + [JsonPropertyName("canvases")] + public IList Canvases { get => field ??= []; set; } +} +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionCanvasListRequest +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// Origin to record on the effective `session.model_change` event for trusted in-process calls. Transport SDK calls are always recorded as `sdk`, regardless of this value. - [JsonPropertyName("source")] - public ModelChangeSource? Source { get; set; } - - /// Output verbosity level to request for supported models. - [JsonPropertyName("verbosity")] - public Verbosity? Verbosity { get; set; } } -/// Immediate acknowledgement and Auto preference snapshot after a switch request. This result never implies that a pending preference committed. +/// Open canvas instance snapshot. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelSwitchAutoTierResult +public sealed class OpenCanvasInstance { - /// Auto preference currently claimed by an in-progress activation. Null means the activation is returning to provider-default routing. - [JsonPropertyName("activatingAutoTier")] - public AutoTier? ActivatingAutoTier { get; set; } + /// Provider-local canvas identifier. + [JsonPropertyName("canvasId")] + public string CanvasId { get; set; } = string.Empty; - /// Auto preference currently committed for the session. - [JsonPropertyName("effectiveAutoTier")] - public AutoTier? EffectiveAutoTier { get; set; } + /// Owning provider identifier. + [JsonPropertyName("extensionId")] + public string ExtensionId { get; set; } = string.Empty; - /// Latest unclaimed Auto preference waiting for a future user turn. - [JsonPropertyName("pendingAutoTier")] - public AutoTier? PendingAutoTier { get; set; } + /// Owning extension display name, when available. + [JsonPropertyName("extensionName")] + public string? ExtensionName { get; set; } - /// Immediate request status. `pending` means accepted but not committed. + /// Host-local PNG path for the canvas icon, when supplied. + [JsonPropertyName("icon")] + public string? Icon { get; set; } + + /// Input supplied when the instance was opened. + [JsonPropertyName("input")] + public JsonElement? Input { get; set; } + + /// Stable caller-supplied canvas instance identifier. + [JsonPropertyName("instanceId")] + public string InstanceId { get; set; } = string.Empty; + + /// Provider-supplied status text. [JsonPropertyName("status")] - public ModelSwitchAutoTierStatus Status { get; set; } + public string? Status { get; set; } - /// Earlier unclaimed preference replaced by this request. This can be present with either status, including when selecting the effective preference cancels pending work. - [JsonPropertyName("supersededAutoTier")] - public AutoTier? SupersededAutoTier { get; set; } + /// Rendered title. + [JsonPropertyName("title")] + public string? Title { get; set; } + + /// URL for web-rendered canvases. + [JsonPropertyName("url")] + public string? Url { get; set; } } -/// An Auto preference request for the session. This updates Auto configuration only; it does not change the selected model to `auto`. +/// Live open-canvas snapshot. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelSwitchAutoTierRequest +public sealed class CanvasListOpenResult { - /// Auto preference to activate when a future user turn using the `auto` model safely mints a replacement model and token pair. Pass null to return to provider-default Auto routing. - [JsonPropertyName("autoTier")] - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public AutoTier? AutoTier { get; set; } + /// Currently open canvas instances. + [JsonPropertyName("openCanvases")] + public IList OpenCanvases { get => field ??= []; set; } +} +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionCanvasListOpenRequest +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// Origin to record on the effective `session.model_change` event. Defaults to `sdk` when omitted. - [JsonPropertyName("source")] - public ModelChangeSource? Source { get; set; } } -/// Managed, repository, and CLI model overrides to overlay onto the session at startup. +/// Canvas open parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelApplyStartupOverlayRequest +internal sealed class CanvasOpenRequest { - /// Effective default Auto routing preference from user and managed settings. Applies only to fresh sessions and never replaces a per-session selection. - [JsonPropertyName("autoTier")] - public AutoTier? AutoTier { get; set; } - - /// Model explicitly selected by the CLI, when provided. - [JsonPropertyName("cliModel")] - public string? CliModel { get; set; } - - /// Whether the overlay is being applied while resuming a deferred session. - [JsonPropertyName("deferredResume")] - public bool? DeferredResume { get; set; } - - /// Model required by device-managed policy, when configured. - [JsonPropertyName("deviceManagedModel")] - public string? DeviceManagedModel { get; set; } - - /// Context tier paired with the effective organization-managed model. Applies only when that concrete managed model is selected; it is ignored for Auto and for CLI, resume, or user overrides. - [JsonPropertyName("managedContextTier")] - public string? ManagedContextTier { get; set; } - - /// Reasoning effort paired with the effective organization-managed model. Applies only when that concrete managed model is selected; it is ignored for Auto and for CLI, resume, or user overrides. - [JsonPropertyName("managedReasoningEffort")] - public string? ManagedReasoningEffort { get; set; } - - /// Startup default model from the enterprise policy helper, when configured. Weakest of the managed sources: it applies only when neither device nor server policy names a model, and an explicit user selection still wins. - [JsonPropertyName("policyHelperModel")] - public string? PolicyHelperModel { get; set; } - - /// Auto routing preference selected by repository settings, when configured. Applied only when the overlay selects the Auto model; beside a concrete model it stays dormant. - [JsonPropertyName("repoAutoTier")] - public string? RepoAutoTier { get; set; } + /// Provider-local canvas identifier. + [JsonPropertyName("canvasId")] + public string CanvasId { get; set; } = string.Empty; - /// Context tier selected by repository settings, when configured. - [JsonPropertyName("repoContextTier")] - public string? RepoContextTier { get; set; } + /// Owning provider identifier. Optional when the canvasId is unique across providers; required to disambiguate when multiple providers register the same canvasId. + [JsonPropertyName("extensionId")] + public string? ExtensionId { get; set; } - /// Model selected by repository settings, when configured. - [JsonPropertyName("repoModel")] - public string? RepoModel { get; set; } + /// Canvas open input. + [JsonPropertyName("input")] + public JsonElement? Input { get; set; } - /// Provider owning the repository-selected model. Omitted repository provider identity retains legacy bare-model behavior. - [JsonPropertyName("repoModelProviderId")] - public string? RepoModelProviderId { get; set; } + /// Caller-supplied stable instance identifier. + [JsonPropertyName("instanceId")] + public string InstanceId { get; set; } = string.Empty; - /// Reasoning effort selected by repository settings, when configured. - [JsonPropertyName("repoReasoningEffort")] - public string? RepoReasoningEffort { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Model required by server-managed policy, when configured. - [JsonPropertyName("serverManagedModel")] - public string? ServerManagedModel { get; set; } +/// Canvas close parameters. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class CanvasCloseRequest +{ + /// Open canvas instance identifier. + [JsonPropertyName("instanceId")] + public string InstanceId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// RPC data type for SessionModelClearStartupSeed operations. +/// Canvas action invocation result. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionModelClearStartupSeedResult +public sealed class CanvasActionInvokeResult { - /// Gets or sets the cleared value. - [JsonPropertyName("cleared")] - public bool Cleared { get; set; } + /// Provider-supplied action result. + [JsonPropertyName("result")] + public JsonElement? Result { get; set; } } -/// RPC data type for SessionModelClearStartupSeed operations. +/// Canvas action invocation parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionModelClearStartupSeedRequest +internal sealed class CanvasActionInvokeRequest { - /// Gets or sets the expectedModel value. - [JsonPropertyName("expectedModel")] - public string ExpectedModel { get; set; } = string.Empty; + /// Action name to invoke. + [JsonPropertyName("actionName")] + public string ActionName { get; set; } = string.Empty; - /// Provider captured with the startup seed; omission matches only an unqualified seed. - [JsonPropertyName("expectedProviderId")] - public string? ExpectedProviderId { get; set; } + /// Action input. + [JsonPropertyName("input")] + public JsonElement? Input { get; set; } + + /// Open canvas instance identifier. + [JsonPropertyName("instanceId")] + public string InstanceId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// The applied host allowlist and effective session model policy after intersection. +/// Internal canvas provider registration parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelSetAllowedModelsResult +internal sealed class CanvasProviderRegisterRequest { - /// Normalized host allowlist. Omitted when the host restriction was cleared, or when a relay client does not return the host policy. - [JsonPropertyName("allowedModels")] - public IList? AllowedModels { get; set; } + /// Canvas contributions supplied by the provider. + [JsonPropertyName("canvases")] + public IList Canvases { get => field ??= []; set; } - /// Effective exact IDs or repository policy patterns after applying the host restriction. Omitted by relay clients that do not return the host policy. - [JsonPropertyName("effectiveAllowedModels")] - public IList? EffectiveAllowedModels { get; set; } + /// Connection identifier for callback routing. + [JsonPropertyName("connectionId")] + public string ConnectionId { get; set; } = string.Empty; - /// Effective deterministic fallback model, when the policy defines one. - [JsonPropertyName("fallbackModel")] - public string? FallbackModel { get; set; } + /// Provider metadata supplied by the host. + [JsonPropertyName("info")] + public JsonElement Info { get; set; } - /// Selected session model after reconciling a now-disallowed concrete selection. - [JsonPropertyName("modelId")] - public string? ModelId { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Host-supplied exact model selection IDs to allow for this running session. CAPI IDs are intersected with repository `.github/allowed_models.txt` policy; provider-qualified IDs remain exempt from repository-only policy but are restricted by this host list. Omit or pass null to clear the host restriction; an explicit empty or disjoint list is rejected. Validation and pre-selection fallback failures preserve the previous restriction. Failures after a fallback selection commits retain the new restriction and selected model; callers should inspect current session state after such an error. +/// Internal canvas provider unregistration parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelSetAllowedModelsRequest +internal sealed class CanvasProviderUnregisterRequest { - /// Exact model IDs to permit, or null to clear the host restriction. - [JsonPropertyName("allowedModels")] - public IList? AllowedModels { get; set; } + /// Connection identifier to unregister. + [JsonPropertyName("connectionId")] + public string ConnectionId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// 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. +/// Machine-readable workflow run failure. +/// Polymorphic base type discriminated by type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelSetReasoningEffortResult +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "type", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(WorkflowRunFailureWorkflowLimitReached), "workflow_limit_reached")] +[JsonDerivedType(typeof(WorkflowRunFailureWorkflowResumeDeclined), "workflow_resume_declined")] +[JsonDerivedType(typeof(WorkflowRunFailureWorkflowDurableFailure), "workflow_durable_failure")] +[JsonDerivedType(typeof(WorkflowRunFailureWorkflowAccountingIncomplete), "workflow_accounting_incomplete")] +[JsonDerivedType(typeof(WorkflowRunFailureWorkflowProviderDisconnected), "workflow_provider_disconnected")] +public partial class WorkflowRunFailure { - /// Reasoning effort level recorded on the session after the update. - [JsonPropertyName("reasoningEffort")] - public string ReasoningEffort { get; set; } = string.Empty; + /// The type discriminator. + [JsonPropertyName("type")] + public virtual string Type { get; set; } = string.Empty; } -/// Reasoning effort level to apply to the currently selected model. + +/// The workflow_limit_reached variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelSetReasoningEffortRequest +public partial class WorkflowRunFailureWorkflowLimitReached : WorkflowRunFailure { - /// Reasoning effort level to apply to the currently selected model. The host is responsible for validating the value against the model's supported levels before calling. - [JsonPropertyName("reasoningEffort")] - public string ReasoningEffort { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Type => "workflow_limit_reached"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Resource ceiling that stopped the run. + [JsonPropertyName("kind")] + public required WorkflowRunFailureKind Kind { get; set; } + + /// Workflow run identifier. + [JsonPropertyName("runId")] + public required string RunId { get; set; } + + /// Suggested larger ceiling when the runtime can derive one safely. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("suggestedValue")] + public double? SuggestedValue { get; set; } + + /// Approved effective ceiling that was reached. + [JsonPropertyName("value")] + public required double Value { get; set; } } -/// Availability of a server-advertised routing preference. +/// The workflow_resume_declined variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AutoTierStatus +public partial class WorkflowRunFailureWorkflowResumeDeclined : WorkflowRunFailure { - /// Whether the provider permits selecting this preference. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } - - /// Human-readable explanation of availability. - [JsonPropertyName("message")] - public string? Message { get; set; } + /// + [JsonIgnore] + public override string Type => "workflow_resume_declined"; - /// Extensible machine-readable unavailability reason. + /// Human-readable reason the resume did not proceed. [JsonPropertyName("reason")] - public string? Reason { get; set; } + public required string Reason { get; set; } + + /// Workflow run identifier whose changed limits were declined. + [JsonPropertyName("runId")] + public required string RunId { get; set; } } -/// A server-advertised routing preference. Identifiers and execution types are extensible. +/// The workflow_durable_failure variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AutoTierDescriptor +public partial class WorkflowRunFailureWorkflowDurableFailure : WorkflowRunFailure { - /// Description displayed beside the preference. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; - - /// Human-readable label, not a routing identifier. - [JsonPropertyName("displayName")] - public string DisplayName { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Type => "workflow_durable_failure"; - /// Opaque routing identifier transmitted unchanged to the provider. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Stable failure code. + [JsonPropertyName("code")] + public required string Code { get; set; } - /// Current account-specific availability. - [JsonPropertyName("status")] - public AutoTierStatus Status { get => field ??= new(); set; } + /// Execution-critical durable operation that failed. + [JsonPropertyName("operation")] + public required WorkflowDurableOperation Operation { get; set; } - /// Execution kind; this client supports `auto` preferences on the Auto model. - [JsonPropertyName("type")] - public string Type { get; set; } = string.Empty; + /// Workflow run identifier. + [JsonPropertyName("runId")] + public required string RunId { get; set; } } -/// Account-bound discovery metadata for the virtual `auto` model. +/// The run stopped because its usage accounting could not be completed. +/// The workflow_accounting_incomplete variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AutoTierMetadata +public partial class WorkflowRunFailureWorkflowAccountingIncomplete : WorkflowRunFailure { - /// Provider-default preference, used only when no explicit preference exists. - [JsonPropertyName("defaultTier")] - public string DefaultTier { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Type => "workflow_accounting_incomplete"; - /// Provider that supplied this metadata, when the catalog is provider-attributed. - [JsonPropertyName("providerId")] - public string? ProviderId { get; set; } + /// Confirmed usage in nano-AIU, representing the floor of what the run spent. + [JsonPropertyName("drainedNanoAiu")] + public required long DrainedNanoAiu { get; set; } - /// Routing preferences in the server's presentation order. - [JsonPropertyName("tiers")] - public IList Tiers { get => field ??= []; set; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public required string RunId { get; set; } } -/// Cost-category metadata for a CAPI model. +/// The extension that owns the workflow disconnected while the run was executing, so the host halted it. The run's journaled subagent results are preserved so a resume can reuse them. +/// The workflow_provider_disconnected variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionModelPriceCategory +public partial class WorkflowRunFailureWorkflowProviderDisconnected : WorkflowRunFailure { - /// CAPI model identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Type => "workflow_provider_disconnected"; - /// Cost category assigned to the model. - [JsonPropertyName("priceCategory")] - public ModelPickerPriceCategory PriceCategory { get; set; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public required string RunId { get; set; } } -/// One model provider available to the session — the model analog of the account `ProviderDescriptor`. Opaque id/label/kind plus a stable ordering; central code never branches on kind. +/// Durable metadata describing who initiated a workflow pause. +/// Polymorphic base type discriminated by type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderDescriptor +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "type", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(WorkflowPauseInfoUser), "user")] +[JsonDerivedType(typeof(WorkflowPauseInfoCheckpoint), "checkpoint")] +public partial class WorkflowPauseInfo { - /// Opaque, stable provider id, stamped onto every model this provider returns. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// The type discriminator. + [JsonPropertyName("type")] + public virtual string Type { get; set; } = string.Empty; +} - /// The neutral provider kind. - [JsonPropertyName("kind")] - public ModelProviderKind Kind { get; set; } - /// Human-readable menu label, owned by the runtime so every consumer renders identical text. - [JsonPropertyName("label")] - public string Label { get; set; } = string.Empty; +/// The user variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class WorkflowPauseInfoUser : WorkflowPauseInfo +{ + /// + [JsonIgnore] + public override string Type => "user"; +} - /// Stable ordering key for presenting providers in a deterministic sequence. - [JsonPropertyName("ordering")] - public long Ordering { get; set; } +/// The checkpoint variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class WorkflowPauseInfoCheckpoint : WorkflowPauseInfo +{ + /// + [JsonIgnore] + public override string Type => "checkpoint"; + + /// Stable author-defined checkpoint key that initiated the pause. + [JsonPropertyName("key")] + public required string Key { get; set; } } -/// The list of models available to this session. +/// Complete current or terminal workflow run envelope. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionModelList +public sealed class WorkflowRunResult { - /// Ordered Auto routing preferences discovered for this session's account. - [JsonPropertyName("auto")] - public AutoTierMetadata? Auto { get; set; } + /// One-based execution attempt represented by this envelope. Absent before the first attempt starts or when returned by an older runtime. + [JsonPropertyName("attempt")] + public long? Attempt { get; set; } - /// Available models, ordered with the most preferred default first. Includes both Copilot (CAPI) models and any registry BYOK models; a BYOK model appears under its provider-qualified selection id (`provider/id`). - [JsonPropertyName("list")] - public IList List { get => field ??= []; set; } + /// Error message for an errored run. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Cost categories for the full CAPI catalog, including picker-disabled models that Auto may select. Metadata only; entries absent from `list` are not manually selectable. - [JsonPropertyName("modelPriceCategories")] - public IList? ModelPriceCategories { get; set; } + /// Machine-readable failure details for a halted or errored run. + [JsonPropertyName("failure")] + public WorkflowRunFailure? Failure { get; set; } - /// The model providers available to this session, in ordering order, resolved from the account roster; empty when no provider is entitled (logged out / seatless). Each model in `list` carries its own provider reference; this roster gives the deterministic provider sequence and lets a consumer group by provider without deriving ordering from the model list. Central code never branches on a provider kind. - [JsonPropertyName("providers")] - public IList? Providers { get; set; } + /// Structured pause initiator metadata for a paused attempt. + [JsonPropertyName("pauseInfo")] + public WorkflowPauseInfo? PauseInfo { get; set; } - /// Per-quota snapshots returned alongside the model list, keyed by quota type. - [JsonPropertyName("quotaSnapshots")] - public IDictionary? QuotaSnapshots { get; set; } + /// Reason for a halted or cancelled run. + [JsonPropertyName("reason")] + public string? Reason { get; set; } + + /// Completed workflow result. + [JsonPropertyName("result")] + public JsonElement? Result { get; set; } + + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; + + /// Partial journal and progress snapshot for a halted, cancelled, or errored run. + [JsonPropertyName("snapshot")] + public JsonElement? Snapshot { get; set; } + + /// Current or terminal workflow run status. + [JsonPropertyName("status")] + public WorkflowRunStatus Status { get; set; } } -/// RPC data type for SessionModelList operations. +/// Wire-only per-invocation workflow resource ceiling overrides. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionModelListRequest +public sealed class WorkflowRunLimits { - /// If true, bypasses the per-session model list cache and re-fetches from CAPI. - [JsonPropertyName("skipCache")] - public bool? SkipCache { get; set; } + /// Maximum AI credits consumed by workflow subagents and their descendants. The post-paid ceiling is soft: parallel turns can settle beyond it before the run stops. + [JsonPropertyName("maxAiCredits")] + public double? MaxAiCredits { get; set; } + + /// Maximum number of workflow subagents that may run concurrently. + [JsonPropertyName("maxConcurrentSubagents")] + public long? MaxConcurrentSubagents { get; set; } + + /// Maximum total number of workflow subagents that may be admitted. + [JsonPropertyName("maxTotalSubagents")] + public long? MaxTotalSubagents { get; set; } + + /// Maximum accumulated active-execution time in seconds. Active execution includes the entire extension body, subprocess waits, queued-agent waits, and sleeps; time between resumed attempts is not counted. + [JsonPropertyName("timeoutSeconds")] + public double? TimeoutSeconds { get; set; } } -/// RPC data type for SessionModelListRequestWithSession operations. +/// Options controlling workflow invocation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionModelListRequestWithSession +public sealed class WorkflowRunOptions { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Per-invocation resource ceiling overrides. + [JsonPropertyName("limits")] + public WorkflowRunLimits? Limits { get; set; } - /// If true, bypasses the per-session model list cache and re-fetches from CAPI. - [JsonPropertyName("skipCache")] - public bool? SkipCache { get; set; } + /// Whether to emit workflow phase names to the session transcript. + [JsonPropertyName("logPhaseNames")] + public bool? LogPhaseNames { get; set; } + + /// Whether to notify the originating session when the workflow completes. + [JsonPropertyName("notifyOnComplete")] + public bool? NotifyOnComplete { get; set; } + + /// Run identifier whose journal and progress should seed this resumed run. + [JsonPropertyName("resumeFromRunId")] + public string? ResumeFromRunId { get; set; } } -/// Identifies the target session. +/// Parameters for invoking a registered workflow. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionModeGetRequest +internal sealed class WorkflowRunRequest { + /// Workflow input value. + [JsonPropertyName("args")] + public JsonElement Args { get; set; } + + /// Registered workflow name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Workflow invocation options. + [JsonPropertyName("options")] + public WorkflowRunOptions? Options { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Outcome of a session mode change, including any model switch it triggered and follow-up the host must perform. +/// Resolved persisted workflow identity and resumed run envelope. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModeSetResult +public sealed class WorkflowResumeResult { - /// Whether the host should arm an interactive continuation after the mode change. - [JsonPropertyName("armInteractiveContinuation")] - public bool? ArmInteractiveContinuation { get; set; } + /// Terminal resumed run envelope. + [JsonPropertyName("run")] + public WorkflowRunResult Run { get => field ??= new(); set; } - /// Compaction confirmation required before the mode change can complete. - [JsonPropertyName("confirmation")] - public ModelSwitchConfirmation? Confirmation { get; set; } - - /// Whether the host must defer implementing the requested mode change. - [JsonPropertyName("deferImplementation")] - public bool? DeferImplementation { get; set; } - - /// Deprecation warnings associated with the model selected by the mode change. - [JsonPropertyName("deprecationWarnings")] - public IList? DeprecationWarnings { get; set; } - - /// User-facing outcome message for the model switch triggered by the mode change. - [JsonPropertyName("message")] - public string? Message { get; set; } - - /// Whether the requested mode was applied to the session. False only when an 'expectedMode' precondition did not hold, in which case any model change reported alongside it was still applied. - [JsonPropertyName("modeApplied")] - public bool? ModeApplied { get; set; } - - /// Whether applying the mode changed the active model. - [JsonPropertyName("modelChanged")] - public bool ModelChanged { get; set; } - - /// Lifecycle status of the requested mode change. - [JsonPropertyName("status")] - public string Status { get; set; } = string.Empty; - - /// User-facing warning produced while applying the mode change. - [JsonPropertyName("warning")] - public string? Warning { get; set; } + /// Persisted workflow name resolved for the resumed run. + [JsonPropertyName("workflowName")] + public string WorkflowName { get; set; } = string.Empty; } -/// Agent interaction mode to apply to the session. +/// Parameters for resuming a workflow run from its persisted identity. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModeSetRequest +internal sealed class WorkflowResumeRequest { - /// Explicit response to a model-switch compaction preflight. - [JsonPropertyName("compactionDecision")] - public string? CompactionDecision { get; set; } - - /// Mode the session must currently be in for the change to apply. When set and the session is in a different mode the request is a no-op and reports status 'unchanged'. - [JsonPropertyName("expectedMode")] - public SessionMode? ExpectedMode { get; set; } - - /// Session whose plan-mode base state should be inherited. - [JsonPropertyName("inheritPlanBaseFromSessionId")] - public string? InheritPlanBaseFromSessionId { get; set; } - - /// The session mode the agent is operating in. - [JsonPropertyName("mode")] - public SessionMode Mode { get; set; } - - /// Whether the selected plan model should be persisted. - [JsonPropertyName("persistPlanSelection")] - public bool? PersistPlanSelection { get; set; } - - /// Settings context used when persisting the selected plan model. - [JsonPropertyName("pickerSettingsContext")] - public ModelPickerSettingsContext? PickerSettingsContext { get; set; } - - /// Context tier to use with the dedicated plan model. - [JsonPropertyName("planContextTier")] - public string? PlanContextTier { get; set; } - - /// Action to perform when leaving plan mode. - [JsonPropertyName("planExitAction")] - public string? PlanExitAction { get; set; } - - /// Dedicated model to use in plan mode, when configured. - [JsonPropertyName("planModel")] - public string? PlanModel { get; set; } - - /// Whether a dedicated plan model is configured. - [JsonPropertyName("planModelConfigured")] - public bool? PlanModelConfigured { get; set; } + /// Optional per-invocation resource ceiling overrides. + [JsonPropertyName("limits")] + public WorkflowRunLimits? Limits { get; set; } - /// Provider owning planModel. Omit for legacy bare-model selection. - [JsonPropertyName("planModelProviderId")] - public string? PlanModelProviderId { get; set; } + /// Whether to emit workflow phase names to the session transcript. + [JsonPropertyName("logPhaseNames")] + public bool? LogPhaseNames { get; set; } - /// Reasoning effort to use with the dedicated plan model. - [JsonPropertyName("planReasoningEffort")] - public string? PlanReasoningEffort { get; set; } + /// Whether to notify the originating session when the workflow completes. + [JsonPropertyName("notifyOnComplete")] + public bool? NotifyOnComplete { get; set; } - /// Whether leaving plan mode should restore the session's previous model. - [JsonPropertyName("restorePlanModel")] - public bool? RestorePlanModel { get; set; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// The session's friendly name, or null when not yet set. +/// Options for an internal tool-originated workflow invocation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class NameGetResult +internal sealed class WorkflowToolRunOptions { - /// The session name (user-set or auto-generated), or null if not yet set. - [JsonPropertyName("name")] - public string? Name { get; set; } -} + /// Per-invocation resource ceiling overrides. + [JsonPropertyName("limits")] + public WorkflowRunLimits? Limits { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionNameGetRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Run identifier whose journal and progress should seed this resumed run. + [JsonPropertyName("resumeFromRunId")] + public string? ResumeFromRunId { get; set; } } -/// New friendly name to apply to the session. +/// Internal parameters for invoking a registered workflow from a tool. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class NameSetRequest +internal sealed class WorkflowToolRunRequest { - /// New session name (1–100 characters, trimmed of leading/trailing whitespace). - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [MaxLength(100)] + /// Workflow input value. + [JsonPropertyName("args")] + public JsonElement Args { get; set; } + + /// Registered workflow name. [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} - -/// Indicates whether the auto-generated summary was applied as the session's name. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class NameSetAutoResult -{ - /// Whether the auto-generated summary was persisted. False if the session already has a user-set name, the summary normalized to empty, or the session does not have a workspace. - [JsonPropertyName("applied")] - public bool Applied { get; set; } -} + /// Tool-originated workflow invocation options. + [JsonPropertyName("options")] + public WorkflowToolRunOptions? Options { get; set; } -/// Auto-generated session summary to apply as the session's name when no user-set name exists. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class NameSetAutoRequest -{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - /// Auto-generated session summary. Empty/whitespace-only values are ignored; values are trimmed before persisting. - [JsonPropertyName("summary")] - public string Summary { get; set; } = string.Empty; + /// Opaque identifier of the originating tool call. + [JsonPropertyName("toolCallId")] + public string? ToolCallId { get; set; } } -/// Existence, contents, and resolved path of the session plan file. +/// Internal parameters for resuming a workflow run from a tool. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PlanReadResult +internal sealed class WorkflowToolResumeRequest { - /// The content of the plan file, or null if it does not exist. - [JsonPropertyName("content")] - public string? Content { get; set; } - - /// Whether the plan file exists in the workspace. - [JsonPropertyName("exists")] - public bool Exists { get; set; } + /// Optional per-invocation resource ceiling overrides. + [JsonPropertyName("limits")] + public WorkflowRunLimits? Limits { get; set; } - /// Absolute file path of the plan file, or null if workspace is not enabled. - [JsonPropertyName("path")] - public string? Path { get; set; } -} + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPlanReadRequest -{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; -} - -/// Replacement contents to write to the session plan file. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PlanUpdateRequest -{ - /// The new content for the plan file. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Opaque identifier of the originating tool call. + [JsonPropertyName("toolCallId")] + public string? ToolCallId { get; set; } } -/// Identifies the target session. +/// Parameters for retrieving a workflow run. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPlanDeleteRequest +internal sealed class WorkflowGetRunRequest { + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// A single todo row read from the session SQL `todos` table. All fields are optional because the SQL schema is best-effort and the agent may not have populated every column. +/// Declared or approved workflow resource ceilings. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PlanSqlTodosRow +public sealed class WorkflowDeclaredLimits { - /// Todo creation time, as stored by the session SQL schema's `datetime('now')` default: `YYYY-MM-DD HH:MM:SS` in UTC. Lets clients attribute todos to the work item that created them (e.g. scoping a goal's progress to the todos it produced) rather than to the whole session. - [JsonPropertyName("createdAt")] - public string? CreatedAt { get; set; } - - /// Todo description. - [JsonPropertyName("description")] - public string? Description { get; set; } + /// Maximum AI credits consumed by subagents and descendants. + [JsonPropertyName("maxAiCredits")] + public double? MaxAiCredits { get; set; } - /// Todo identifier. - [JsonPropertyName("id")] - public string? Id { get; set; } + /// Maximum concurrently active subagents. + [JsonPropertyName("maxConcurrentSubagents")] + public long? MaxConcurrentSubagents { get; set; } - /// Todo status. - [JsonPropertyName("status")] - public string? Status { get; set; } + /// Maximum total subagents spawned by the run. + [JsonPropertyName("maxTotalSubagents")] + public long? MaxTotalSubagents { get; set; } - /// Todo title. - [JsonPropertyName("title")] - public string? Title { get; set; } + /// Maximum accumulated active execution time in seconds. + [JsonPropertyName("timeoutSeconds")] + public double? TimeoutSeconds { get; set; } } -/// Todo rows read from the session SQL database. Empty when no session database is available. +/// Durable workflow resource consumption. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PlanReadSqlTodosResult +public sealed class WorkflowRunConsumed { - /// Rows from the session SQL todos table, ordered by creation time with insertion order used to break ties when available and id used for WITHOUT ROWID tables. - [JsonPropertyName("rows")] - public IList Rows { get => field ??= []; set; } -} + /// Accumulated active execution time in milliseconds. + [JsonPropertyName("activeMs")] + public long ActiveMs { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPlanReadSqlTodosRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// AI usage consumed by the run in nano-AIU. + [JsonPropertyName("nanoAiu")] + public long NanoAiu { get; set; } + + /// Total subagents spawned by the run. + [JsonPropertyName("subagents")] + public long Subagents { get; set; } } -/// A single dependency edge read from the session SQL `todo_deps` table, indicating that one todo must complete before another. +/// Current workflow phase identity. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PlanSqlTodoDependency +public sealed class WorkflowCurrentPhase { - /// ID of the todo it depends on. - [JsonPropertyName("dependsOn")] - public string DependsOn { get; set; } = string.Empty; + /// Current phase identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// ID of the todo that has the dependency. - [JsonPropertyName("todoId")] - public string TodoId { get; set; } = string.Empty; + /// Zero-based declared phase ordinal, or null for an undeclared phase. + [JsonPropertyName("ordinal")] + public long? Ordinal { get; set; } } -/// Todo rows + dependency edges read from the session SQL database. +/// Prompt-safe terminal workflow outcome. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PlanReadSqlTodosWithDependenciesResult +public sealed class WorkflowRunTerminal { - /// Edges from the session SQL todo_deps table. Empty when no database, no todo_deps table, or the SELECT failed. Read independently from `rows`, so a broken todo_deps table does not affect the rows result and vice versa. - [JsonPropertyName("dependencies")] - public IList Dependencies { get => field ??= []; set; } + /// Human-readable terminal error. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Rows from the session SQL todos table, ordered by creation time with insertion order used to break ties when available and id used for WITHOUT ROWID tables. Empty when no database, no todos table, or the SELECT failed. - [JsonPropertyName("rows")] - public IList Rows { get => field ??= []; set; } + /// Machine-readable terminal failure. + [JsonPropertyName("failure")] + public WorkflowRunFailure? Failure { get; set; } + + /// Pause initiator metadata, or null when the run did not pause. + [JsonPropertyName("pauseInfo")] + public WorkflowPauseInfo? PauseInfo { get; set; } + + /// Human-readable terminal reason. + [JsonPropertyName("reason")] + public string? Reason { get; set; } + + /// Prompt-safe preview of the completed result. + [JsonPropertyName("resultPreview")] + public string? ResultPreview { get; set; } } -/// Identifies the target session. +/// Durable workflow run summary with read-time live overlays. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPlanReadSqlTodosWithDependenciesRequest +public sealed class WorkflowRunSummary { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Epoch milliseconds when the current active segment started, or null while inactive. + [JsonPropertyName("activeSegmentStartedAt")] + public long? ActiveSegmentStartedAt { get; set; } -/// RPC data type for WorkspacesGetWorkspaceResultWorkspace operations. -public sealed class WorkspacesGetWorkspaceResultWorkspace -{ - /// Current Git branch. - [JsonPropertyName("branch")] - public string? Branch { get; set; } + /// Approved effective resource ceilings, or null until approved. + [JsonPropertyName("approved")] + public WorkflowDeclaredLimits? Approved { get; set; } - /// Whether the per-session Chronicle upgrade prompt was dismissed for the workspace. - [JsonPropertyName("chronicle_sync_dismissed")] - public bool? ChronicleSyncDismissed { get; set; } + /// Whether the durable run state currently passes runtime resume eligibility checks. + [JsonPropertyName("canResume")] + public bool CanResume { get; set; } - /// Name of the client that created the workspace. - [JsonPropertyName("client_name")] - public string? ClientName { get; set; } + /// Epoch milliseconds when the run completed, or null while nonterminal. + [JsonPropertyName("completedAt")] + public long? CompletedAt { get; set; } - /// Timestamp when the workspace was created. - [JsonPropertyName("created_at")] - public DateTimeOffset? CreatedAt { get; set; } + /// Durable resource consumption. + [JsonPropertyName("consumed")] + public WorkflowRunConsumed Consumed { get => field ??= new(); set; } - /// Current working directory associated with the workspace. - [JsonPropertyName("cwd")] - public string? Cwd { get; set; } + /// Epoch milliseconds when the run was created. + [JsonPropertyName("createdAt")] + public long CreatedAt { get; set; } - /// Git repository root associated with the workspace. - [JsonPropertyName("git_root")] - public string? GitRoot { get; set; } + /// Current phase identity, or null before any phase is entered. + [JsonPropertyName("currentPhase")] + public WorkflowCurrentPhase? CurrentPhase { get; set; } - /// Allowed values for the `WorkspacesWorkspaceDetailsHostType` enumeration. - [JsonPropertyName("host_type")] - public WorkspacesWorkspaceDetailsHostType? HostType { get; set; } + /// Resource ceilings declared by the workflow. + [JsonPropertyName("declaredLimits")] + public WorkflowDeclaredLimits DeclaredLimits { get => field ??= new(); set; } - /// Stable workspace identifier. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Number of phases declared by the workflow. + [JsonPropertyName("declaredPhaseCount")] + public long DeclaredPhaseCount { get; set; } - /// Most recent Mission Control event identifier observed for the workspace. - [JsonPropertyName("mc_last_event_id")] - public string? McLastEventId { get; set; } + /// Human-readable workflow description. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; - /// Mission Control session identifier associated with the workspace. - [JsonPropertyName("mc_session_id")] - public string? McSessionId { get; set; } + /// Number of direct workflow agents currently live. + [JsonPropertyName("liveAgentCount")] + public long LiveAgentCount { get; set; } - /// Mission Control task identifier associated with the workspace. - [JsonPropertyName("mc_task_id")] - public string? McTaskId { get; set; } + /// Epoch milliseconds when this live-overlay snapshot was observed. + [JsonPropertyName("observedAt")] + public long ObservedAt { get; set; } - /// Workspace display name. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// Monotonic durable run revision. + [JsonPropertyName("revision")] + public long Revision { get; set; } - /// Whether the workspace session can be steered remotely. - [JsonPropertyName("remote_steerable")] - public bool? RemoteSteerable { get; set; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; - /// Repository identifier associated with the workspace. - [JsonPropertyName("repository")] - public string? Repository { get; set; } + /// Epoch milliseconds when execution first started, or null before start. + [JsonPropertyName("startedAt")] + public long? StartedAt { get; set; } - /// Number of persisted summaries in the workspace. - [JsonPropertyName("summary_count")] - public long? SummaryCount { get; set; } + /// Current workflow run status. + [JsonPropertyName("status")] + public WorkflowRunStatus Status { get; set; } - /// Timestamp when the workspace was last updated. - [JsonPropertyName("updated_at")] - public DateTimeOffset? UpdatedAt { get; set; } + /// Terminal run outcome, or null while nonterminal. + [JsonPropertyName("terminal")] + public WorkflowRunTerminal? Terminal { get; set; } - /// Whether the workspace name was explicitly chosen by the user. - [JsonPropertyName("user_named")] - public bool? UserNamed { get; set; } -} + /// Total direct workflow agents spawned across all attempts. + [JsonPropertyName("totalSpawnedAgentCount")] + public long TotalSpawnedAgentCount { get; set; } -/// Current workspace metadata for the session, including its absolute filesystem path when available. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesGetWorkspaceResult -{ - /// Absolute filesystem path to the workspace directory. Omitted when the session has no workspace (e.g. remote sessions). - [JsonPropertyName("path")] - public string? Path { get; set; } + /// Epoch milliseconds when the durable run was last updated. + [JsonPropertyName("updatedAt")] + public long UpdatedAt { get; set; } - /// Current workspace metadata, or null if not available. - [JsonPropertyName("workspace")] - public WorkspacesGetWorkspaceResultWorkspace? Workspace { get; set; } + /// Registered workflow name. + [JsonPropertyName("workflowName")] + public string WorkflowName { get; set; } = string.Empty; } -/// Identifies the target session. +/// A page of workflow runs in durable creation order. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionWorkspacesGetWorkspaceRequest +public sealed class WorkflowListRunsResult { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Whether terminal runs newer than this page exist. + [JsonPropertyName("hasMoreNewer")] + public bool? HasMoreNewer { get; set; } -/// Workspace metadata fields to update. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesUpdateMetadataRequest -{ - /// Opaque workspace context supplied by the session host. - [JsonPropertyName("context")] - public JsonElement? Context { get; set; } + /// Newest terminal-run cursor in this page, or null when the terminal window is empty. + [JsonPropertyName("newestSeq")] + public long? NewestSeq { get; set; } - /// Optional workspace display name override. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// Oldest terminal-run cursor in this page, or null when the terminal window is empty. + [JsonPropertyName("oldestSeq")] + public long? OldestSeq { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Number of terminal runs older than this page. + [JsonPropertyName("omittedOlder")] + public long? OmittedOlder { get; set; } + + /// Workflow run summaries in durable creation order. + [JsonPropertyName("runs")] + public IList Runs { get => field ??= []; set; } } -/// Optional session context used when creating a local workspace. +/// Parameters for paging workflow runs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesEnsureRequest +internal sealed class WorkflowListRunsRequest { - /// Opaque workspace context supplied by the session host. - [JsonPropertyName("context")] - public JsonElement? Context { get; set; } + /// Exclusive forward cursor. + [JsonPropertyName("afterSeq")] + public long? AfterSeq { get; set; } + + /// Exclusive backward cursor. + [JsonPropertyName("beforeSeq")] + public long? BeforeSeq { get; set; } + + /// Maximum terminal runs to return. Defaults to 200 and is capped at 500. + [JsonPropertyName("limit")] + public int? Limit { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Relative paths of files stored in the session workspace files directory. +/// Prompt-safe durable identity and live status for a direct workflow agent. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesListFilesResult +public sealed class WorkflowAgentSummary { - /// Slash-separated relative file paths in the workspace files directory. - [JsonPropertyName("files")] - public IList Files { get => field ??= []; set; } -} + /// Accumulated active agent time in milliseconds. + [JsonPropertyName("activeMs")] + public long ActiveMs { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionWorkspacesListFilesRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} - -/// Contents of the requested workspace file as a UTF-8 string. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesReadFileResult -{ - /// File content as a UTF-8 string. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; -} + /// Prompt-safe live activity text. + [JsonPropertyName("activity")] + public string? Activity { get; set; } -/// Relative path of the workspace file to read. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesReadFileRequest -{ - /// Slash-separated relative path within the workspace files directory. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Stable direct-agent identifier. + [JsonPropertyName("agentId")] + public string AgentId { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Registered agent type. + [JsonPropertyName("agentType")] + public string AgentType { get; set; } = string.Empty; -/// Relative path and UTF-8 content for the workspace file to create or overwrite. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesCreateFileRequest -{ - /// File content to write as a UTF-8 string. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// Epoch milliseconds when the agent completed. + [JsonPropertyName("completedAt")] + public long? CompletedAt { get; set; } - /// Slash-separated relative path within the workspace files directory. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Friendly, non-unique name intended for display. + [JsonPropertyName("displayName")] + public string? DisplayName { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Friendly, non-unique name intended for display. + [JsonPropertyName("label")] + public string Label { get; set; } = string.Empty; -/// Filesystem metadata for a path in the session workspace files directory. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesStatFileResult -{ - /// Creation time in Unix epoch milliseconds. - [JsonPropertyName("birthtimeMs")] - public double BirthtimeMs { get; set; } + /// Phase identifier active when the agent was launched, or null. + [JsonPropertyName("phaseId")] + public string? PhaseId { get; set; } - /// Whether the path identifies a directory. - [JsonPropertyName("isDirectory")] - public bool IsDirectory { get; set; } + /// Model requested when the agent was launched. + [JsonPropertyName("requestedModel")] + public string? RequestedModel { get; set; } - /// Whether the path identifies a regular file. - [JsonPropertyName("isFile")] - public bool IsFile { get; set; } + /// Concrete model resolved for the agent. + [JsonPropertyName("resolvedModel")] + public string? ResolvedModel { get; set; } - /// Last modification time in Unix epoch milliseconds. - [JsonPropertyName("mtimeMs")] - public double MtimeMs { get; set; } + /// Owning workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; - /// Size in bytes. - [JsonPropertyName("size")] - public double Size { get; set; } -} + /// Epoch milliseconds when the agent started. + [JsonPropertyName("startedAt")] + public long? StartedAt { get; set; } -/// Relative path of the workspace file or directory to inspect. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesStatFileRequest -{ - /// Slash-separated relative path within the workspace files directory. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Current durable or live agent status. + [JsonPropertyName("status")] + public string Status { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Tool-call identifier that launched the agent. + [JsonPropertyName("toolCallId")] + public string ToolCallId { get; set; } = string.Empty; } -/// Directory to create within the session workspace files directory. +/// Durable lifecycle and timing for one workflow phase. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesCreateDirectoryRequest +public sealed class WorkflowPhaseObservation { - /// Slash-separated relative path within the workspace files directory. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; - - /// Whether to create missing parent directories. Defaults to false. - [JsonPropertyName("recursive")] - public bool? Recursive { get; set; } + /// Completed active time accumulated by this phase in milliseconds. + [JsonPropertyName("accumulatedActiveMs")] + public long AccumulatedActiveMs { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Epoch milliseconds when this phase completed; for a skipped phase, the synthetic skip timestamp (equal to `startedAt`). + [JsonPropertyName("completedAt")] + public long? CompletedAt { get; set; } -/// File or directory to remove from the session workspace files directory. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesRemovePathRequest -{ - /// Whether a missing path should be treated as success. Defaults to false. - [JsonPropertyName("force")] - public bool? Force { get; set; } + /// Current live active time for this phase in milliseconds. + [JsonPropertyName("currentActiveMs")] + public long CurrentActiveMs { get; set; } - /// Slash-separated relative path within the workspace files directory. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Optional human-readable phase detail. + [JsonPropertyName("detail")] + public string? Detail { get; set; } - /// Whether to remove directory contents recursively. Defaults to false. - [JsonPropertyName("recursive")] - public bool? Recursive { get; set; } + /// Number of times execution entered this phase. + [JsonPropertyName("entryCount")] + public long EntryCount { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Phase identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; -/// Source and destination paths for a rename within the session workspace files directory. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesRenamePathRequest -{ - /// Slash-separated destination path relative to the workspace files directory. - [JsonPropertyName("destination")] - public string Destination { get; set; } = string.Empty; + /// Most recent run attempt that entered this phase, or `0` if the phase has never been entered. + [JsonPropertyName("lastEnteredRunAttempt")] + public long LastEnteredRunAttempt { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Direct agents in this phase that are currently live. + [JsonPropertyName("liveAgentCount")] + public long LiveAgentCount { get; set; } - /// Slash-separated source path relative to the workspace files directory. - [JsonPropertyName("source")] - public string Source { get; set; } = string.Empty; -} + /// Zero-based declared phase ordinal, or null for an undeclared phase. + [JsonPropertyName("ordinal")] + public long? Ordinal { get; set; } -/// Workspace checkpoint metadata with assigned number, human-readable title, and checkpoint filename. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesCheckpoints -{ - /// Filename of the checkpoint within the workspace checkpoints directory. - [JsonPropertyName("filename")] - public string Filename { get; set; } = string.Empty; + /// Epoch milliseconds when this phase first started; for a skipped phase, the synthetic skip timestamp (equal to `completedAt`). + [JsonPropertyName("startedAt")] + public long? StartedAt { get; set; } - /// Checkpoint number assigned by the workspace manager. - [JsonPropertyName("number")] - public long Number { get; set; } + /// Derived lifecycle state of the phase. + [JsonPropertyName("status")] + public WorkflowPhaseStatus Status { get; set; } - /// Human-readable checkpoint title. + /// Human-readable phase title. [JsonPropertyName("title")] public string Title { get; set; } = string.Empty; -} -/// Workspace checkpoints in chronological order; empty when the workspace is not enabled. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesListCheckpointsResult -{ - /// Workspace checkpoints in chronological order. Empty when workspace is not enabled. - [JsonPropertyName("checkpoints")] - public IList Checkpoints { get => field ??= []; set; } + /// Total direct agents associated with this phase. + [JsonPropertyName("totalAgentCount")] + public long TotalAgentCount { get; set; } } -/// Identifies the target session. +/// One durable workflow progress record. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionWorkspacesListCheckpointsRequest +public sealed class WorkflowProgressLine { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Resume attempt that emitted this record. + [JsonPropertyName("attempt")] + public long Attempt { get; set; } -/// Checkpoint content as a UTF-8 string, or null when the checkpoint or workspace is missing. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesReadCheckpointResult -{ - /// Checkpoint content as a UTF-8 string, or null when the checkpoint or workspace is missing. - [JsonPropertyName("content")] - public string? Content { get; set; } + /// Progress record kind. + [JsonPropertyName("kind")] + public WorkflowLogLineKind Kind { get; set; } + + /// Phase active when the record was emitted, or null before any phase. + [JsonPropertyName("phaseId")] + public string? PhaseId { get; set; } + + /// Epoch milliseconds when the record was persisted. + [JsonPropertyName("recordedAt")] + public long RecordedAt { get; set; } + + /// Global monotonic sequence number within the run. + [JsonPropertyName("seq")] + public long Seq { get; set; } + + /// Prompt-safe progress text. + [JsonPropertyName("text")] + public string Text { get; set; } = string.Empty; } -/// Checkpoint number to read. +/// A bidirectional page of workflow progress. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesReadCheckpointRequest +public sealed class WorkflowProgressPage { - /// Checkpoint number to read. - [JsonPropertyName("number")] - public long Number { get; set; } + /// Whether progress records newer than this page exist. + [JsonPropertyName("hasMoreNewer")] + public bool HasMoreNewer { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Whether progress records older than this page exist. + [JsonPropertyName("hasMoreOlder")] + public bool HasMoreOlder { get; set; } -/// Metadata for the persisted summary. -public sealed class WorkspacesAddSummaryResultSummary -{ -} + /// Newest sequence number in this page, or null when empty. + [JsonPropertyName("newestSeq")] + public long? NewestSeq { get; set; } -/// Refreshed metadata for the containing workspace. -public sealed class WorkspacesAddSummaryResultWorkspace -{ + /// Oldest sequence number in this page, or null when empty. + [JsonPropertyName("oldestSeq")] + public long? OldestSeq { get; set; } + + /// Progress records in sequence order. + [JsonPropertyName("records")] + public IList Records { get => field ??= []; set; } + + /// Run revision reflected by this page. + [JsonPropertyName("revision")] + public long Revision { get; set; } } -/// Persisted summary metadata and refreshed workspace metadata. +/// Full workflow run observability detail. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesAddSummaryResult +public sealed class WorkflowRunDetail { - /// Metadata for the persisted summary. - [JsonPropertyName("summary")] - public WorkspacesAddSummaryResultSummary? Summary { get; set; } + /// Epoch milliseconds when the current active segment started, or null while inactive. + [JsonPropertyName("activeSegmentStartedAt")] + public long? ActiveSegmentStartedAt { get; set; } - /// Refreshed metadata for the containing workspace. - [JsonPropertyName("workspace")] - public WorkspacesAddSummaryResultWorkspace? Workspace { get; set; } + /// Durable identities and live statuses for direct workflow agents. + [JsonPropertyName("agents")] + public IList Agents { get => field ??= []; set; } + + /// Approved effective resource ceilings, or null until approved. + [JsonPropertyName("approved")] + public WorkflowDeclaredLimits? Approved { get; set; } + + /// Whether the durable run state currently passes runtime resume eligibility checks. + [JsonPropertyName("canResume")] + public bool CanResume { get; set; } + + /// Epoch milliseconds when the run completed, or null while nonterminal. + [JsonPropertyName("completedAt")] + public long? CompletedAt { get; set; } + + /// Durable resource consumption. + [JsonPropertyName("consumed")] + public WorkflowRunConsumed Consumed { get => field ??= new(); set; } + + /// Epoch milliseconds when the run was created. + [JsonPropertyName("createdAt")] + public long CreatedAt { get; set; } + + /// Current phase identity, or null before any phase is entered. + [JsonPropertyName("currentPhase")] + public WorkflowCurrentPhase? CurrentPhase { get; set; } + + /// Resource ceilings declared by the workflow. + [JsonPropertyName("declaredLimits")] + public WorkflowDeclaredLimits DeclaredLimits { get => field ??= new(); set; } + + /// Number of phases declared by the workflow. + [JsonPropertyName("declaredPhaseCount")] + public long DeclaredPhaseCount { get; set; } + + /// Human-readable workflow description. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; + + /// Number of direct workflow agents currently live. + [JsonPropertyName("liveAgentCount")] + public long LiveAgentCount { get; set; } + + /// Epoch milliseconds when this live-overlay snapshot was observed. + [JsonPropertyName("observedAt")] + public long ObservedAt { get; set; } + + /// Lifecycle and timing observations for each workflow phase. + [JsonPropertyName("phases")] + public IList Phases { get => field ??= []; set; } + + /// Bidirectional page of durable workflow progress. + [JsonPropertyName("progress")] + public WorkflowProgressPage Progress { get => field ??= new(); set; } + + /// Monotonic durable run revision. + [JsonPropertyName("revision")] + public long Revision { get; set; } + + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; + + /// Epoch milliseconds when execution first started, or null before start. + [JsonPropertyName("startedAt")] + public long? StartedAt { get; set; } + + /// Current workflow run status. + [JsonPropertyName("status")] + public WorkflowRunStatus Status { get; set; } + + /// Terminal run outcome, or null while nonterminal. + [JsonPropertyName("terminal")] + public WorkflowRunTerminal? Terminal { get; set; } + + /// Total direct workflow agents spawned across all attempts. + [JsonPropertyName("totalSpawnedAgentCount")] + public long TotalSpawnedAgentCount { get; set; } + + /// Epoch milliseconds when the durable run was last updated. + [JsonPropertyName("updatedAt")] + public long UpdatedAt { get; set; } + + /// Registered workflow name. + [JsonPropertyName("workflowName")] + public string WorkflowName { get; set; } = string.Empty; } -/// Compaction summary checkpoint to persist. +/// Parameters for paging workflow progress. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesAddSummaryRequest +internal sealed class WorkflowGetRunProgressRequest { - /// Markdown summary content to persist. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// Exclusive forward cursor. + [JsonPropertyName("afterSeq")] + public long? AfterSeq { get; set; } + + /// Exclusive backward cursor. + [JsonPropertyName("beforeSeq")] + public long? BeforeSeq { get; set; } + + /// Maximum records to return. Defaults to 200 and is capped at 500. + [JsonPropertyName("limit")] + public int? Limit { get; set; } + + /// Optional phase identifier used to scope records and cursors. + [JsonPropertyName("phaseId")] + public string? PhaseId { get; set; } + + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// Summary title shown in checkpoint listings. - [JsonPropertyName("title")] - public string Title { get; set; } = string.Empty; } -/// Rollback point for local workspace summaries. +/// Parameters for cancelling a workflow run. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesTruncateSummariesRequest +internal sealed class WorkflowCancelRequest { - /// Number of newest summaries to keep. - [JsonPropertyName("keepCount")] - public long KeepCount { get; set; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Autopilot objective file content, or null when missing. +/// Parameters for pausing a running workflow. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesReadAutopilotObjectiveResult +internal sealed class WorkflowPauseRequest { - /// Autopilot objective file content, or null when missing. - [JsonPropertyName("content")] - public string? Content { get; set; } -} + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionWorkspacesReadAutopilotObjectiveRequest -{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of writing the autopilot objective file. +/// RPC data type for SessionWorkflowPauseAtCheckpoint operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesWriteAutopilotObjectiveResult +internal sealed class SessionWorkflowPauseAtCheckpointResult { - /// Filesystem operation performed. - [JsonPropertyName("operation")] - public string Operation { get; set; } = string.Empty; + /// Whether this execution attempt must pause or may continue. + [JsonPropertyName("action")] + public WorkflowPauseCheckpointAction Action { get; set; } } -/// Autopilot objective file content to persist. +/// Parameters for an owned durable pause checkpoint. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesWriteAutopilotObjectiveRequest +internal sealed class WorkflowPauseCheckpointRequest { - /// Autopilot objective file content. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// Opaque token identifying the execution attempt that reached the checkpoint. + [JsonPropertyName("executionToken")] + public string ExecutionToken { get; set; } = string.Empty; + + /// Stable author-defined checkpoint key. + [JsonPropertyName("key")] + public string Key { get; set; } = string.Empty; + + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of deleting the autopilot objective file. +/// Acknowledgement that a workflow request was accepted. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesDeleteAutopilotObjectiveResult +public sealed class WorkflowAckResult { - /// True when a file was deleted. - [JsonPropertyName("deleted")] - public bool Deleted { get; set; } } -/// Identifies the target session. +/// One ordered workflow progress line. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionWorkspacesDeleteAutopilotObjectiveRequest +public sealed class WorkflowLogLine { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Progress line kind. + [JsonPropertyName("kind")] + public WorkflowLogLineKind Kind { get; set; } -/// Whether the autopilot objective file exists. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesAutopilotObjectiveExistsResult -{ - /// True when the objective file exists. - [JsonPropertyName("exists")] - public bool Exists { get; set; } + /// Monotonic sequence number within the workflow run. + [JsonPropertyName("seq")] + public long Seq { get; set; } + + /// Progress text. + [JsonPropertyName("text")] + public string Text { get; set; } = string.Empty; } -/// Identifies the target session. +/// Parameters for recording workflow progress. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionWorkspacesAutopilotObjectiveExistsRequest +internal sealed class WorkflowLogRequest { + /// Opaque token identifying the current workflow execution attempt. + [JsonPropertyName("executionToken")] + public string ExecutionToken { get; set; } = string.Empty; + + /// Ordered progress lines to append. + [JsonPropertyName("lines")] + public IList Lines { get => field ??= []; set; } + + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// RPC data type for WorkspacesSaveLargePasteResultSaved operations. -public sealed class WorkspacesSaveLargePasteResultSaved -{ - /// Filename within the workspace files directory. - [JsonPropertyName("filename")] - public string Filename { get; set; } = string.Empty; - - /// Absolute filesystem path to the saved paste file. - [JsonPropertyName("filePath")] - public string FilePath { get; set; } = string.Empty; - - /// Size of the saved file in bytes. - [JsonPropertyName("sizeBytes")] - public long SizeBytes { get; set; } -} - -/// Descriptor for the saved paste file, or null when the workspace is unavailable. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesSaveLargePasteResult -{ - /// Saved-paste descriptor, or null when the workspace is unavailable (e.g. CCA runtime, non-infinite sessions, remote sessions). - [JsonPropertyName("saved")] - public WorkspacesSaveLargePasteResultSaved? Saved { get; set; } -} - -/// Pasted content to save as a UTF-8 file in the session workspace. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesSaveLargePasteRequest -{ - /// Pasted content to save as a UTF-8 file. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} - -/// Complete text used to generate one session diff. These are display contents, with the same text decoding as the patch, not a file-restore contract. +/// Result of one workflow-scoped subagent call. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspaceDiffContents +public sealed class WorkflowAgentResult { - /// Complete current text read when computing the diff. Omitted for a deleted file; an empty string represents an existing empty file. - [JsonPropertyName("after")] - public string? After { get; set; } - - /// Complete text before the session first changed the file. Omitted when the file did not exist; an empty string represents an existing empty file. - [JsonPropertyName("before")] - public string? Before { get; set; } + /// Agent result, omitted when the agent produced no result. + [JsonPropertyName("result")] + public JsonElement? Result { get; set; } } -/// A single changed file and its unified diff. +/// Options for one workflow-scoped subagent call. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspaceDiffFileChange +public sealed class WorkflowAgentOptions { - /// Type of change represented by this file diff. - [JsonPropertyName("changeType")] - public WorkspaceDiffFileChangeType ChangeType { get; set; } + /// Optional built-in or custom agent name whose definition configures the subagent. + [JsonPropertyName("agent")] + public string? Agent { get; set; } - /// Full text used for this patch, only when includeContents was requested for session mode. Omitted for binary, oversized or unavailable contents, and for fallback results. Read isFallback and isTruncated before treating an absent value as a missing file. - [JsonPropertyName("contents")] - public WorkspaceDiffContents? Contents { get; set; } + /// Optional context tier override for the subagent. + [JsonPropertyName("contextTier")] + public ContextTier? ContextTier { get; set; } - /// Unified diff content for the file. Empty when the diff was truncated. - [JsonPropertyName("diff")] - public string Diff { get; set; } = string.Empty; + /// Optional label distinguishing otherwise identical memoized agent calls. + [JsonPropertyName("label")] + public string? Label { get; set; } - /// Whether the diff content was omitted because it exceeded the per-file size limit. - [JsonPropertyName("isTruncated")] - public bool? IsTruncated { get; set; } + /// Optional model identifier for the subagent. + [JsonPropertyName("model")] + public string? Model { get; set; } - /// Original file path for renamed files. - [JsonPropertyName("oldPath")] - public string? OldPath { get; set; } + /// Optional reasoning effort override for the subagent. + [JsonPropertyName("reasoningEffort")] + public string? ReasoningEffort { get; set; } - /// Path to the changed file, relative to the workspace root when the file lives under it. A file changed outside the workspace root keeps a `../`-relative path, or an absolute path when no relative path exists (for example a different Windows drive). - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Optional JSON Schema for structured agent output. + [JsonPropertyName("schema")] + public JsonElement? Schema { get; set; } } -/// Workspace diff result for the requested mode. +/// Parameters for one workflow-scoped subagent call. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspaceDiffResult +internal sealed class WorkflowAgentRequest { - /// Default branch used for a branch diff, when branch mode was requested. - [JsonPropertyName("baseBranch")] - public string? BaseBranch { get; set; } - - /// Changed files and their unified diffs. - [JsonPropertyName("changes")] - public IList Changes { get => field ??= []; set; } + /// Opaque token identifying the current workflow execution attempt. + [JsonPropertyName("executionToken")] + public string ExecutionToken { get; set; } = string.Empty; - /// Whether the requested diff fell back to unstaged changes, either because branch diff failed or session diff was unavailable. - [JsonPropertyName("isFallback")] - public bool IsFallback { get; set; } + /// Subagent execution options. + [JsonPropertyName("opts")] + public WorkflowAgentOptions Opts { get => field ??= new(); set; } - /// Effective mode used for the returned changes. - [JsonPropertyName("mode")] - public WorkspaceDiffMode Mode { get; set; } + /// Prompt to send to the subagent. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; - /// Diff mode requested by the client. - [JsonPropertyName("requestedMode")] - public WorkspaceDiffMode RequestedMode { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Why the session diff could not be produced, when applicable. Set only when `session` mode was requested and `isFallback` is true, so a client can tell the permanent `file-change-tracking-disabled` apart from the transient `session-busy`, which the same request answers once the session settles. Never set for `unstaged` or `branch` mode, and never `unsupported-remote-session`: a remote session's captures live on its own host, so a `session`-mode diff is rejected for one rather than answered with a controller-side fallback. - [JsonPropertyName("unavailableReason")] - public HistoryRewindUnavailableReason? UnavailableReason { get; set; } + /// Workflow run identifier that owns the subagent. + [JsonPropertyName("workflowRunId")] + public string WorkflowRunId { get; set; } = string.Empty; } -/// Parameters for computing a workspace diff. +/// Result of reading a workflow journal entry. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesDiffRequest +public sealed class WorkflowJournalGetResult { - /// When true, ignore whitespace-only changes (git `--ignore-all-space`). Defaults to false. - [JsonPropertyName("ignoreWhitespace")] - public bool? IgnoreWhitespace { get; set; } - - /// Include the full before/after text used to compute each session diff. Defaults to false; true is accepted only for session mode. Existing capture/read limits still apply, and binary or unavailable contents are not returned. This can substantially increase response size. - [JsonPropertyName("includeContents")] - public bool? IncludeContents { get; set; } + /// Whether the journal contained the requested key. + [JsonPropertyName("hit")] + public bool Hit { get; set; } - /// Diff mode requested by the client. - [JsonPropertyName("mode")] - public required WorkspaceDiffMode Mode { get; set; } + /// Cached JSON result. The hit field distinguishes a cached JSON null from a miss. + [JsonPropertyName("resultJson")] + public JsonElement? ResultJson { get; set; } } -/// Parameters for computing a workspace diff. +/// Parameters for reading a workflow journal entry. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesDiffRequestWithSession +internal sealed class WorkflowJournalGetRequest { - /// When true, ignore whitespace-only changes (git `--ignore-all-space`). Defaults to false. - [JsonPropertyName("ignoreWhitespace")] - public bool? IgnoreWhitespace { get; set; } + /// Opaque token identifying the current workflow execution attempt. + [JsonPropertyName("executionToken")] + public string ExecutionToken { get; set; } = string.Empty; - /// Include the full before/after text used to compute each session diff. Defaults to false; true is accepted only for session mode. Existing capture/read limits still apply, and binary or unavailable contents are not returned. This can substantially increase response size. - [JsonPropertyName("includeContents")] - public bool? IncludeContents { get; set; } + /// Namespaced journal key. + [JsonPropertyName("key")] + public string Key { get; set; } = string.Empty; - /// Diff mode requested by the client. - [JsonPropertyName("mode")] - public WorkspaceDiffMode Mode { get; set; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Current per-window credit limit and consumption for an autopilot objective. +/// Parameters for storing a workflow journal entry. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AutopilotObjectiveCreditLimit +internal sealed class WorkflowJournalPutRequest { - /// Configured AI-credit cap, when one is set. - [JsonPropertyName("credits")] - public double? Credits { get; set; } + /// Opaque token identifying the current workflow execution attempt. + [JsonPropertyName("executionToken")] + public string ExecutionToken { get; set; } = string.Empty; - /// Window consumption in fractional AI credits, for display. - [JsonPropertyName("creditsUsed")] - public double CreditsUsed { get; set; } + /// Namespaced journal key. + [JsonPropertyName("key")] + public string Key { get; set; } = string.Empty; - /// Exact window consumption in non-negative integer nano-AIU, encoded as a decimal string. - [RegularExpression("^[0-9]+$")] - [JsonPropertyName("creditsUsedNanoAiu")] - public string CreditsUsedNanoAiu { get; set; } = string.Empty; + /// JSON result to memoize. + [JsonPropertyName("resultJson")] + public JsonElement ResultJson { get; set; } + + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Public, persistence-independent projection of an autopilot objective. +/// The session's authoritative model snapshot. Auto preference fields are configuration for the virtual `auto` model and do not change the selected model identifier. The context tier reflects `Session.getContextTier()`, restored from the session journal on resume. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AutopilotObjectiveState +public sealed class CurrentModel { - /// Optional summary recorded when the objective completed. - [JsonPropertyName("completionSummary")] - public string? CompletionSummary { get; set; } + /// Auto preference currently claimed by an in-progress activation. Null means the activation is returning to provider-default routing. + [JsonPropertyName("activatingAutoTier")] + public AutoTier? ActivatingAutoTier { get; set; } - /// Exact lifetime AI-credit consumption in non-negative integer nano-AIU, encoded as a decimal string. - [RegularExpression("^[0-9]+$")] - [JsonPropertyName("creditCountNanoAiu")] - public string CreditCountNanoAiu { get; set; } = string.Empty; + /// Auto preference currently committed for the session. This can remain available while another model is selected so a later switch to `auto` can reuse it. + [JsonPropertyName("autoTier")] + public AutoTier? AutoTier { get; set; } - /// Current per-window consumption and optional cap, when a credit-tracking window is present. - [JsonPropertyName("creditLimit")] - public AutopilotObjectiveCreditLimit? CreditLimit { get; set; } + /// Context tier for models that support multiple context-window sizes. + [JsonPropertyName("contextTier")] + public ContextTier? ContextTier { get; set; } - /// Session-local objective identifier. - [JsonPropertyName("id")] - public long Id { get; set; } + /// Currently active model identifier. + [JsonPropertyName("modelId")] + public string? ModelId { get; set; } - /// User-provided objective text. - [JsonPropertyName("objective")] - public string Objective { get; set; } = string.Empty; + /// Latest unclaimed Auto preference waiting for a future user turn. Null means the pending request is returning to provider-default routing. + [JsonPropertyName("pendingAutoTier")] + public AutoTier? PendingAutoTier { get; set; } - /// Optional reason the objective is paused. - [JsonPropertyName("pauseReason")] - public string? PauseReason { get; set; } + /// Captured base model to restore when leaving plan mode. Omitted outside plan mode or when no plan override has captured a base model. Persistent agent model requirements apply to this model rather than the temporary plan model. + [JsonPropertyName("planBaseModelId")] + public string? PlanBaseModelId { get; set; } - /// Current normalized lifecycle status. - [JsonPropertyName("status")] - public AutopilotObjectiveStatus Status { get; set; } + /// Provider of planBaseModelId, when that saved selection is provider-qualified. + [JsonPropertyName("planBaseProviderId")] + public string? PlanBaseProviderId { get; set; } - /// Number of objective turns started. - [JsonPropertyName("turnCount")] - public long TurnCount { get; set; } -} + /// Provider selected for this model. Together with modelId, identifies a catalog entry even when providers serve the same model. Omitted for unattributed legacy selections. + [JsonPropertyName("providerId")] + public string? ProviderId { get; set; } -/// Canonical runtime state for the session's current autopilot objective. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AutopilotObjectiveGetStateResult -{ - /// Current objective state, or `null` when the session has no objective. - [JsonPropertyName("state")] - public AutopilotObjectiveState? State { get; set; } + /// Reasoning effort level currently applied to the active model, when one is set. Reads `Session.getReasoningEffort()` synchronously after `getSelectedModel()` resolves so the two values are reported as a snapshot. + [JsonPropertyName("reasoningEffort")] + public string? ReasoningEffort { get; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionAutopilotObjectiveGetStateRequest +internal sealed class SessionModelGetCurrentRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Characters that, when typed in the composer, should trigger a `completions.request`. Empty when the session has no host-driven completions (e.g. local sessions, or a relay host that does not advertise `completionTriggerCharacters`). +/// RPC data type for ModelSwitchConfirmation operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CompletionsGetTriggerCharactersResult +public sealed class ModelSwitchConfirmation { - /// Trigger characters advertised by the host (e.g. `["@", "#"]`). Empty disables host-driven completions for the session. - [JsonPropertyName("triggerCharacters")] - public IList TriggerCharacters { get => field ??= []; set; } -} + /// Current conversation token count before switching models. + [JsonPropertyName("currentTokens")] + public double CurrentTokens { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionCompletionsGetTriggerCharactersRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Target model token limit used by the compaction preflight. + [JsonPropertyName("targetLimit")] + public double TargetLimit { get; set; } + + /// Display name of the model that requires compaction confirmation. + [JsonPropertyName("targetModelDisplayName")] + public string TargetModelDisplayName { get; set; } = string.Empty; } -/// A single host-driven completion. Accepting an item replaces `[rangeStart, rangeEnd)` (UTF-16 code units) in the composer with `insertText`; when the range is absent, the active token around the cursor is replaced. +/// The model identifier active on the session after the switch. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionCompletionItem +public sealed class ModelSwitchToResult { - /// Text spliced into the composer when the item is accepted. - [JsonPropertyName("insertText")] - public string InsertText { get; set; } = string.Empty; + /// Compaction confirmation projection when status is confirmation_required. + [JsonPropertyName("confirmation")] + public ModelSwitchConfirmation? Confirmation { get; set; } - /// Render-kind hint for the picker row (e.g. `"document"`, `"directory"`), derived from the host's display kind. - [JsonPropertyName("kind")] - public string? Kind { get; set; } + /// True when the switch was deferred (enqueued as a cancellable `/model` command) because a turn was active or another model change was already queued, rather than applied immediately. When true, the session's live model is unchanged until the queued change drains. + [JsonPropertyName("deferred")] + public bool? Deferred { get; set; } - /// Primary display label for the picker row. Falls back to `insertText` when absent. - [JsonPropertyName("label")] - public string? Label { get; set; } + /// Deprecation warnings associated with the selected model or options. + [JsonPropertyName("deprecationWarnings")] + public IList? DeprecationWarnings { get; set; } - /// End (exclusive) of the replacement range in `text`, in UTF-16 code units. - [JsonPropertyName("rangeEnd")] - public long? RangeEnd { get; set; } + /// User-facing outcome message for the model switch. + [JsonPropertyName("message")] + public string? Message { get; set; } - /// Start of the replacement range in `text`, in UTF-16 code units. - [JsonPropertyName("rangeStart")] - public long? RangeStart { get; set; } -} + /// Currently active model identifier after the switch. + [JsonPropertyName("modelId")] + public string? ModelId { get; set; } -/// Host-driven completion items for the current composer input. Empty when the host returns no items or does not support completions. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CompletionsRequestResult -{ - /// Completion items in host-ranked order. - [JsonPropertyName("items")] - public IList Items { get => field ??= []; set; } -} + /// Authoritative model and Auto preference state after an immediate switch. For deferred switches this remains the current state until the queued change drains. + [JsonPropertyName("modelState")] + public CurrentModel? ModelState { get; set; } -/// Request host-driven completions for the current composer input. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CompletionsRequestRequest -{ - /// Cursor offset within `text`, in UTF-16 code units. - [JsonPropertyName("offset")] - public long Offset { get; set; } + /// Persistence failure encountered after applying the model switch. + [JsonPropertyName("persistenceError")] + public string? PersistenceError { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Stable queue item identifier when this request was enqueued. Remains present if the item drains before the response is returned. + [JsonPropertyName("queueId")] + public string? QueueId { get; set; } - /// The full composed composer input. - [JsonPropertyName("text")] - public string Text { get; set; } = string.Empty; -} + /// Lifecycle result for the requested switch. + [JsonPropertyName("status")] + public string? Status { get; set; } -/// Instruction sources loaded for the session, in merge order. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class InstructionsGetSourcesResult -{ - /// Instruction sources for the session. - [JsonPropertyName("sources")] - public IList Sources { get => field ??= []; set; } + /// User-facing warning produced while applying the model switch. + [JsonPropertyName("warning")] + public string? Warning { get; set; } } -/// Identifies the target session. +/// Filesystem and environment context used to resolve model-picker settings. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionInstructionsGetSourcesRequest +public sealed class ModelPickerSettingsContext { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Optional Copilot configuration directory containing persisted settings. + [JsonPropertyName("configDir")] + public string? ConfigDir { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionInstructionsReloadRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Environment variables consulted while resolving model-picker settings. + [JsonPropertyName("environment")] + public IDictionary Environment { get => field ??= new Dictionary(); set; } + + /// User home directory used when resolving persisted settings. + [JsonPropertyName("homeDirectory")] + public string HomeDirectory { get; set; } = string.Empty; } -/// Result of one customization reload component. +/// RPC data type for ModelPickerPersistence operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CustomizationReloadOutcome +public sealed class ModelPickerPersistenceRequest { - /// Reason for a skipped component or description of a failure, when available. - [JsonPropertyName("detail")] - public string? Detail { get; set; } + /// Whether context tier was explicitly selected and should be persisted. + [JsonPropertyName("contextTierExplicit")] + public bool? ContextTierExplicit { get; set; } - /// Whether the component reloaded, was skipped, or failed. - [JsonPropertyName("status")] - public CustomizationReloadStatus Status { get; set; } + /// Whether reasoning effort was explicitly selected and should be persisted. + [JsonPropertyName("reasoningEffortExplicit")] + public bool? ReasoningEffortExplicit { get; set; } - /// Component whose reload was attempted or skipped. - [JsonPropertyName("subsystem")] - public CustomizationReloadSubsystem Subsystem { get; set; } + /// Filesystem and environment context used to resolve settings persistence. + [JsonPropertyName("settingsContext")] + public ModelPickerSettingsContext SettingsContext { get => field ??= new(); set; } } -/// Results of reloading discovered session customizations. Inspect outcomes for reloaded, skipped, or failed subsystems; a rejection may follow partial mutation. Changes to the model-facing prompt and tools apply on the next turn. +/// Target model identifier and optional reasoning effort, summary, capability overrides, and context tier. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CustomizationsReloadResult +public sealed class ModelSwitchToRequest { - /// Errors from any component that could not be refreshed. - [JsonPropertyName("errors")] - public IList Errors { get => field ??= []; set; } + /// Optional Auto routing preference to stage atomically with selecting `auto`. Pass null to return to provider-default Auto routing. This field is rejected when `modelId` is not `auto`. + [JsonPropertyName("autoTier")] + public AutoTier? AutoTier { get; set; } - /// Outcome of each component in reload order; a skipped component was not configured or loaded. - [JsonPropertyName("outcomes")] - public IList Outcomes { get => field ??= []; set; } + /// Explicit response to a model-switch compaction preflight. Omit to request a confirmation projection when compaction is necessary. + [JsonPropertyName("compactionDecision")] + public string? CompactionDecision { get; set; } - /// Warnings from skill discovery. - [JsonPropertyName("warnings")] - public IList Warnings { get => field ??= []; set; } -} + /// Explicit context tier for the selected model. `"default"` / `"long_context"` apply the requested tier; omit this field to use normal model behavior with no explicit tier. + [JsonPropertyName("contextTier")] + public ContextTier? ContextTier { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionCustomizationsReloadRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// When true, defer this switch (enqueue it) if another model change is already queued, even when no turn is active — so it drains last (FIFO) and wins over the already-queued change. Intended for genuine user-initiated model selections; internal restore/reapply switches omit it and apply immediately when no turn is active. When no other model change is queued this has no effect (a switch still applies immediately unless a turn is active). + [JsonPropertyName("deferIfModelChangeQueued")] + public bool? DeferIfModelChangeQueued { get; set; } -/// Indicates whether fleet mode was successfully activated. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class FleetStartResult -{ - /// Whether fleet mode was successfully activated. - [JsonPropertyName("started")] - public bool Started { get; set; } -} + /// Override individual model capabilities resolved by the runtime. + [JsonPropertyName("modelCapabilities")] + public ModelCapabilitiesOverride? ModelCapabilities { get; set; } -/// Parameters for starting fleet orchestration: an optional user prompt combined with the fleet instructions, plus the send options forwarded to the resulting turn. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class FleetStartRequest -{ - /// Optional attachments (files, directories, selections, blobs, GitHub references) to include with the fleet request. - [JsonPropertyName("attachments")] - public IList? Attachments { get; set; } + /// Settings scope used when persisting the selected model. + [JsonPropertyName("modelChangeScope")] + public string? ModelChangeScope { get; set; } - /// If false, this request will not trigger a Premium Request Unit charge. User requests default to billable. - [JsonInclude] - [JsonPropertyName("billable")] - internal bool? Billable { get; set; } + /// Model id to switch to, as returned by `list`. Include providerId to select an exact catalog entry when providers share the id. Without providerId, a bare id retains incumbent selection behavior; a registry-qualified id (`provider/id`, e.g. `acme/claude-sonnet`) targets a BYOK model. + [JsonPropertyName("modelId")] + public required string ModelId { get; set; } - /// Optional user prompt to combine with fleet instructions. - [JsonPropertyName("prompt")] - public string? Prompt { get; set; } + /// Optional settings context and explicit-override flags used to persist a picker selection. + [JsonPropertyName("pickerPersistence")] + public ModelPickerPersistenceRequest? PickerPersistence { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Provider id from the selected list entry's provider reference. Selects this exact provider/model pair; an unavailable pair fails rather than using another provider. Omit for deterministic legacy bare-model selection. + [JsonPropertyName("providerId")] + public string? ProviderId { get; set; } - /// If true, await completion of the agentic loop for this fleet request before returning. Defaults to false. - [JsonPropertyName("wait")] - public bool? Wait { get; set; } -} + /// Reasoning effort level to use for the model. CAPI values are model-defined and validated against the selected model; BYOK providers may define additional values. "none" disables reasoning. Pass null to clear any session effort override and fall back to the model's default. When omitted, the session's current effort is kept. + [JsonPropertyName("reasoningEffort")] + public string? ReasoningEffort { get; set; } -/// Agents available to the session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AgentList -{ - /// Available agents. - [JsonPropertyName("agents")] - public IList Agents { get => field ??= []; set; } -} + /// Reasoning summary mode to request for supported model clients. + [JsonPropertyName("reasoningSummary")] + public ReasoningSummary? ReasoningSummary { get; set; } -/// RPC data type for SessionAgentList operations. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionAgentListRequest -{ - /// When true, request the session's configured built-in agents alongside custom agents. Listing applies feature, context, inclusion, exclusion, and user-disabled-agent policy, but does not evaluate transient invocation requirements such as model availability. Built-in metadata may be omitted when the session cannot project it, such as a relay session. - [JsonPropertyName("includeBuiltInAgents")] - public bool? IncludeBuiltInAgents { get; set; } + /// Optional repository settings scope to persist after the switch commits. + [JsonPropertyName("repoScope")] + public string? RepoScope { get; set; } - /// When true, request authored base prompt text on each AgentInfo. Prompt text may be omitted when unavailable, such as for agents projected through a relay session. - [JsonPropertyName("includePrompt")] - public bool? IncludePrompt { get; set; } -} + /// Require the target to be currently available and enabled before applying the switch. + [JsonPropertyName("requireAvailable")] + public bool? RequireAvailable { get; set; } -/// RPC data type for SessionAgentListRequestWithSession operations. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionAgentListRequestWithSession -{ - /// When true, request the session's configured built-in agents alongside custom agents. Listing applies feature, context, inclusion, exclusion, and user-disabled-agent policy, but does not evaluate transient invocation requirements such as model availability. Built-in metadata may be omitted when the session cannot project it, such as a relay session. - [JsonPropertyName("includeBuiltInAgents")] - public bool? IncludeBuiltInAgents { get; set; } + /// When true, evaluate context-window compaction policy before applying the switch. + [JsonPropertyName("runCompactionPreflight")] + public bool? RunCompactionPreflight { get; set; } - /// When true, request authored base prompt text on each AgentInfo. Prompt text may be omitted when unavailable, such as for agents projected through a relay session. - [JsonPropertyName("includePrompt")] - public bool? IncludePrompt { get; set; } + /// Origin to record on the effective `session.model_change` event for trusted in-process calls. Transport SDK calls are always recorded as `sdk`, regardless of this value. + [JsonPropertyName("source")] + public ModelChangeSource? Source { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Output verbosity level to request for supported models. + [JsonPropertyName("verbosity")] + public Verbosity? Verbosity { get; set; } } -/// An in-memory authored prompt override for an available agent. +/// Target model identifier and optional reasoning effort, summary, capability overrides, and context tier. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AgentSetPromptRequest +internal sealed class ModelSwitchToRequestWithSession { - /// Stable effective agent id. Plugin namespace separators are normalized. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Optional Auto routing preference to stage atomically with selecting `auto`. Pass null to return to provider-default Auto routing. This field is rejected when `modelId` is not `auto`. + [JsonPropertyName("autoTier")] + public AutoTier? AutoTier { get; set; } - /// Replacement authored prompt. Empty text is valid. - [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; + /// Explicit response to a model-switch compaction preflight. Omit to request a confirmation projection when compaction is necessary. + [JsonPropertyName("compactionDecision")] + public string? CompactionDecision { get; set; } + + /// Explicit context tier for the selected model. `"default"` / `"long_context"` apply the requested tier; omit this field to use normal model behavior with no explicit tier. + [JsonPropertyName("contextTier")] + public ContextTier? ContextTier { get; set; } + + /// When true, defer this switch (enqueue it) if another model change is already queued, even when no turn is active — so it drains last (FIFO) and wins over the already-queued change. Intended for genuine user-initiated model selections; internal restore/reapply switches omit it and apply immediately when no turn is active. When no other model change is queued this has no effect (a switch still applies immediately unless a turn is active). + [JsonPropertyName("deferIfModelChangeQueued")] + public bool? DeferIfModelChangeQueued { get; set; } + + /// Override individual model capabilities resolved by the runtime. + [JsonPropertyName("modelCapabilities")] + public ModelCapabilitiesOverride? ModelCapabilities { get; set; } + + /// Settings scope used when persisting the selected model. + [JsonPropertyName("modelChangeScope")] + public string? ModelChangeScope { get; set; } + + /// Model id to switch to, as returned by `list`. Include providerId to select an exact catalog entry when providers share the id. Without providerId, a bare id retains incumbent selection behavior; a registry-qualified id (`provider/id`, e.g. `acme/claude-sonnet`) targets a BYOK model. + [JsonPropertyName("modelId")] + public string ModelId { get; set; } = string.Empty; + + /// Optional settings context and explicit-override flags used to persist a picker selection. + [JsonPropertyName("pickerPersistence")] + public ModelPickerPersistenceRequest? PickerPersistence { get; set; } + + /// Provider id from the selected list entry's provider reference. Selects this exact provider/model pair; an unavailable pair fails rather than using another provider. Omit for deterministic legacy bare-model selection. + [JsonPropertyName("providerId")] + public string? ProviderId { get; set; } + + /// Reasoning effort level to use for the model. CAPI values are model-defined and validated against the selected model; BYOK providers may define additional values. "none" disables reasoning. Pass null to clear any session effort override and fall back to the model's default. When omitted, the session's current effort is kept. + [JsonPropertyName("reasoningEffort")] + public string? ReasoningEffort { get; set; } + + /// Reasoning summary mode to request for supported model clients. + [JsonPropertyName("reasoningSummary")] + public ReasoningSummary? ReasoningSummary { get; set; } + + /// Optional repository settings scope to persist after the switch commits. + [JsonPropertyName("repoScope")] + public string? RepoScope { get; set; } + + /// Require the target to be currently available and enabled before applying the switch. + [JsonPropertyName("requireAvailable")] + public bool? RequireAvailable { get; set; } + + /// When true, evaluate context-window compaction policy before applying the switch. + [JsonPropertyName("runCompactionPreflight")] + public bool? RunCompactionPreflight { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Origin to record on the effective `session.model_change` event for trusted in-process calls. Transport SDK calls are always recorded as `sdk`, regardless of this value. + [JsonPropertyName("source")] + public ModelChangeSource? Source { get; set; } + + /// Output verbosity level to request for supported models. + [JsonPropertyName("verbosity")] + public Verbosity? Verbosity { get; set; } } -/// The currently selected custom agent, or null when using the default agent. +/// Immediate acknowledgement and Auto preference snapshot after a switch request. This result never implies that a pending preference committed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AgentGetCurrentResult +public sealed class ModelSwitchAutoTierResult { - /// Currently selected custom agent, or null if using the default agent. - [JsonPropertyName("agent")] - public AgentInfo? Agent { get; set; } + /// Auto preference currently claimed by an in-progress activation. Null means the activation is returning to provider-default routing. + [JsonPropertyName("activatingAutoTier")] + public AutoTier? ActivatingAutoTier { get; set; } + + /// Auto preference currently committed for the session. + [JsonPropertyName("effectiveAutoTier")] + public AutoTier? EffectiveAutoTier { get; set; } + + /// Latest unclaimed Auto preference waiting for a future user turn. + [JsonPropertyName("pendingAutoTier")] + public AutoTier? PendingAutoTier { get; set; } + + /// Immediate request status. `pending` means accepted but not committed. + [JsonPropertyName("status")] + public ModelSwitchAutoTierStatus Status { get; set; } + + /// Earlier unclaimed preference replaced by this request. This can be present with either status, including when selecting the effective preference cancels pending work. + [JsonPropertyName("supersededAutoTier")] + public AutoTier? SupersededAutoTier { get; set; } } -/// Identifies the target session. +/// An Auto preference request for the session. This updates Auto configuration only; it does not change the selected model to `auto`. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionAgentGetCurrentRequest +internal sealed class ModelSwitchAutoTierRequest { + /// Auto preference to activate when a future user turn using the `auto` model safely mints a replacement model and token pair. Pass null to return to provider-default Auto routing. + [JsonPropertyName("autoTier")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public AutoTier? AutoTier { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; -} -/// The newly selected custom agent. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AgentSelectResult -{ - /// The newly selected custom agent. - [JsonPropertyName("agent")] - public AgentInfo Agent { get => field ??= new(); set; } + /// Origin to record on the effective `session.model_change` event. Defaults to `sdk` when omitted. + [JsonPropertyName("source")] + public ModelChangeSource? Source { get; set; } } -/// Name of the custom agent to select for subsequent turns. +/// Managed, repository, and CLI model overrides to overlay onto the session at startup. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AgentSelectRequest +internal sealed class ModelApplyStartupOverlayRequest { - /// Name of the custom agent to select. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Effective default Auto routing preference from user and managed settings. Applies only to fresh sessions and never replaces a per-session selection. + [JsonPropertyName("autoTier")] + public AutoTier? AutoTier { get; set; } + + /// Model explicitly selected by the CLI, when provided. + [JsonPropertyName("cliModel")] + public string? CliModel { get; set; } + + /// Whether the overlay is being applied while resuming a deferred session. + [JsonPropertyName("deferredResume")] + public bool? DeferredResume { get; set; } + + /// Model required by device-managed policy, when configured. + [JsonPropertyName("deviceManagedModel")] + public string? DeviceManagedModel { get; set; } + + /// Context tier paired with the effective organization-managed model. Applies only when that concrete managed model is selected; it is ignored for Auto and for CLI, resume, or user overrides. + [JsonPropertyName("managedContextTier")] + public string? ManagedContextTier { get; set; } + + /// Reasoning effort paired with the effective organization-managed model. Applies only when that concrete managed model is selected; it is ignored for Auto and for CLI, resume, or user overrides. + [JsonPropertyName("managedReasoningEffort")] + public string? ManagedReasoningEffort { get; set; } + + /// Startup default model from the enterprise policy helper, when configured. Weakest of the managed sources: it applies only when neither device nor server policy names a model, and an explicit user selection still wins. + [JsonPropertyName("policyHelperModel")] + public string? PolicyHelperModel { get; set; } + + /// Auto routing preference selected by repository settings, when configured. Applied only when the overlay selects the Auto model; beside a concrete model it stays dormant. + [JsonPropertyName("repoAutoTier")] + public string? RepoAutoTier { get; set; } + + /// Context tier selected by repository settings, when configured. + [JsonPropertyName("repoContextTier")] + public string? RepoContextTier { get; set; } + + /// Model selected by repository settings, when configured. + [JsonPropertyName("repoModel")] + public string? RepoModel { get; set; } + + /// Provider owning the repository-selected model. Omitted repository provider identity retains legacy bare-model behavior. + [JsonPropertyName("repoModelProviderId")] + public string? RepoModelProviderId { get; set; } + + /// Reasoning effort selected by repository settings, when configured. + [JsonPropertyName("repoReasoningEffort")] + public string? RepoReasoningEffort { get; set; } + + /// Model required by server-managed policy, when configured. + [JsonPropertyName("serverManagedModel")] + public string? ServerManagedModel { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Identifies the target session. +/// RPC data type for SessionModelClearStartupSeed operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionAgentDeselectRequest +internal sealed class SessionModelClearStartupSeedResult +{ + /// Gets or sets the cleared value. + [JsonPropertyName("cleared")] + public bool Cleared { get; set; } +} + +/// RPC data type for SessionModelClearStartupSeed operations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionModelClearStartupSeedRequest { + /// Gets or sets the expectedModel value. + [JsonPropertyName("expectedModel")] + public string ExpectedModel { get; set; } = string.Empty; + + /// Provider captured with the startup seed; omission matches only an unqualified seed. + [JsonPropertyName("expectedProviderId")] + public string? ExpectedProviderId { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Custom agents available to the session after reloading definitions from disk. +/// The applied host allowlist and effective session model policy after intersection. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AgentReloadResult +public sealed class ModelSetAllowedModelsResult { - /// Reloaded custom agents. - [JsonPropertyName("agents")] - public IList Agents { get => field ??= []; set; } + /// Normalized host allowlist. Omitted when the host restriction was cleared, or when a relay client does not return the host policy. + [JsonPropertyName("allowedModels")] + public IList? AllowedModels { get; set; } + + /// Effective exact IDs or repository policy patterns after applying the host restriction. Omitted by relay clients that do not return the host policy. + [JsonPropertyName("effectiveAllowedModels")] + public IList? EffectiveAllowedModels { get; set; } + + /// Effective deterministic fallback model, when the policy defines one. + [JsonPropertyName("fallbackModel")] + public string? FallbackModel { get; set; } + + /// Selected session model after reconciling a now-disallowed concrete selection. + [JsonPropertyName("modelId")] + public string? ModelId { get; set; } } -/// Identifies the target session. +/// Host-supplied exact model selection IDs to allow for this running session. CAPI IDs are intersected with repository `.github/allowed_models.txt` policy; provider-qualified IDs remain exempt from repository-only policy but are restricted by this host list. Omit or pass null to clear the host restriction; an explicit empty or disjoint list is rejected. Validation and pre-selection fallback failures preserve the previous restriction. Failures after a fallback selection commits retain the new restriction and selected model; callers should inspect current session state after such an error. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionAgentReloadRequest +internal sealed class ModelSetAllowedModelsRequest { + /// Exact model IDs to permit, or null to clear the host restriction. + [JsonPropertyName("allowedModels")] + public IList? AllowedModels { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Identifier assigned to the newly started background agent task. +/// 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. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksStartAgentResult +public sealed class ModelSetReasoningEffortResult { - /// Generated agent ID for the background task. - [JsonPropertyName("agentId")] - public string AgentId { get; set; } = string.Empty; + /// Reasoning effort level recorded on the session after the update. + [JsonPropertyName("reasoningEffort")] + public string ReasoningEffort { get; set; } = string.Empty; } -/// Agent type, prompt, name, and optional description and model override for the new task. +/// Reasoning effort level to apply to the currently selected model. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksStartAgentRequest +internal sealed class ModelSetReasoningEffortRequest { - /// Type of agent to start (e.g., 'explore', 'task', 'general-purpose'). - [JsonPropertyName("agentType")] - public string AgentType { get; set; } = string.Empty; - - /// Short description of the task. - [JsonPropertyName("description")] - public string? Description { get; set; } - - /// Optional model override. - [JsonPropertyName("model")] - public string? Model { get; set; } - - /// Friendly, non-unique name used when displaying the agent. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - /// Task prompt for the agent. - [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; + /// Reasoning effort level to apply to the currently selected model. The host is responsible for validating the value against the model's supported levels before calling. + [JsonPropertyName("reasoningEffort")] + public string ReasoningEffort { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Tracked task union returned by task APIs, containing an agent, client, or shell task. -/// Polymorphic base type discriminated by type. +/// Reasoning-summaries presentation policy for the session's current model. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "type", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(TaskInfoAgent), "agent")] -[JsonDerivedType(typeof(TaskInfoClient), "client")] -[JsonDerivedType(typeof(TaskInfoShell), "shell")] -public partial class TaskInfo +public sealed class ModelGetReasoningSummariesResult { - /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { get; set; } = string.Empty; -} + /// Whether the supplied `persistedShowReasoning` setting only restates the model default. This reports a redundancy, not a standing instruction to delete the setting: it stays true for as long as the setting and the model default agree, including for a user who later chose that value deliberately. It exists for a one-time migration of a legacy setting written before the model default made it redundant, so a host that acts on it owns the cleanup and must gate it behind its own persisted `done` marker, delete only a setting whose value still matches the one it read, and never repeat the deletion on a later launch. Under an off-by-default reasoning-summaries experiment an explicit `showReasoning: true` opt-in also reads as redundant on a summary-producing model, so an ungated host would keep deleting that opt-in and hide summaries again on every launch. + [JsonPropertyName("clearRedundantSetting")] + public bool ClearRedundantSetting { get; set; } + /// Whether the session's reasoning-summaries experiment assignment has settled. False means this read does not wait for a pending assignment and reports the experiment-derived fields conservatively: `showHiddenHint` is false and `visibility` falls back to `persistedShowReasoning`, or is omitted when none was supplied. A host that wants the assigned answer re-reads until this is true. + [JsonPropertyName("experimentSettled")] + public bool ExperimentSettled { get; set; } -/// Tracked background agent task metadata, including IDs, status, timing, agent type, prompt, model, result, and latest response. -/// The agent variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskInfoAgent : TaskInfo -{ - /// - [JsonIgnore] - public override string Type => "agent"; + /// Whether the selected model shows reasoning summaries by default. + [JsonPropertyName("modelDefaultShowsSummaries")] + public bool ModelDefaultShowsSummaries { get; set; } - /// ISO 8601 timestamp when the current active period began. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("activeStartedAt")] - public DateTimeOffset? ActiveStartedAt { get; set; } + /// False until the session has a model catalog and a selected model. This read never resolves the catalog itself, and the runtime emits no readiness notification, so repeating this call alone leaves `ready` false indefinitely on a session that has neither listed models nor run a turn. `session.model.list` is the prerequisite and the signal: it resolves and caches the catalog for the session, so a successful return means the next read is ready. The exception is a session that resolves models without a Copilot catalog, that is a BYOK registry or an alternate provider: `session.model.list` returns an empty list without caching a catalog, so `ready` stays false and this policy is unavailable there. While false, every model-policy field is false and `visibility` is omitted; `experimentSettled` is independent of readiness and still reports the assignment state. + [JsonPropertyName("ready")] + public bool Ready { get; set; } - /// Accumulated active execution time in milliseconds. - [JsonConverter(typeof(MillisecondsTimeSpanConverter))] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("activeTimeMs")] - public TimeSpan? ActiveTime { get; set; } + /// Whether turns on this session produce reasoning summaries, honouring an explicit session reasoning-summary override. + [JsonPropertyName("sessionShowsSummaries")] + public bool SessionShowsSummaries { get; set; } - /// Type of agent running this task. - [JsonPropertyName("agentType")] - public required string AgentType { get; set; } + /// Whether to show the hint that reasoning summaries are hidden. Always false while `experimentSettled` is false. + [JsonPropertyName("showHiddenHint")] + public bool ShowHiddenHint { get; set; } - /// Whether the task is currently in the original sync wait and can be moved to background mode. False once it is already backgrounded, idle, finished, or no longer has a promotable sync waiter. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("canPromoteToBackground")] - public bool? CanPromoteToBackground { get; set; } + /// Whether the selected model emits reasoning headers. + [JsonPropertyName("showReasoningHeaders")] + public bool ShowReasoningHeaders { get; set; } - /// ISO 8601 timestamp when the task finished. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("completedAt")] - public DateTimeOffset? CompletedAt { get; set; } + /// Default reasoning visibility, and only meaningful once `ready` is true: false when the session produces no summaries, else the persisted setting, else the experiment's default. While `ready` is true it is omitted only when all three are unavailable, that is when the session produces summaries, no `persistedShowReasoning` was supplied, and `experimentSettled` is false. While `ready` is false there is no model policy to report and this field is always omitted, so an absent value alone does not imply a pending experiment. + [JsonPropertyName("visibility")] + public bool? Visibility { get; set; } +} - /// Short description of the task. - [JsonPropertyName("description")] - public required string Description { get; set; } +/// Host presentation state the reasoning-summaries policy depends on. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ModelGetReasoningSummariesRequest +{ + /// The host's persisted `showReasoning` user setting, omitted when unset. + [JsonPropertyName("persistedShowReasoning")] + public bool? PersistedShowReasoning { get; set; } - /// Friendly, non-unique name intended for display. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("displayName")] - public string? DisplayName { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Error message when the task failed. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("error")] - public string? Error { get; set; } + /// Whether the host is currently showing reasoning summaries. Defaults to false. + [JsonPropertyName("summariesVisible")] + public bool? SummariesVisible { get; set; } +} - /// Whether task execution is synchronously awaited or managed in the background. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("executionMode")] - public TaskExecutionMode? ExecutionMode { get; set; } +/// Availability of a server-advertised routing preference. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class AutoTierStatus +{ + /// Whether the provider permits selecting this preference. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } - /// Unique task identifier. - [JsonPropertyName("id")] - public required string Id { get; set; } + /// Human-readable explanation of availability. + [JsonPropertyName("message")] + public string? Message { get; set; } - /// ISO 8601 timestamp when the agent entered idle state. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("idleSince")] - public DateTimeOffset? IdleSince { get; set; } + /// Extensible machine-readable unavailability reason. + [JsonPropertyName("reason")] + public string? Reason { get; set; } +} - /// Most recent response text from the agent. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("latestResponse")] - public string? LatestResponse { get; set; } +/// A server-advertised routing preference. Identifiers and execution types are extensible. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class AutoTierDescriptor +{ + /// Description displayed beside the preference. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; - /// Requested model override for the task when specified. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("model")] - public string? Model { get; set; } + /// Human-readable label, not a routing identifier. + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } = string.Empty; - /// Most recent prompt delivered to the agent. Updated whenever the agent receives a follow-up message. - [JsonPropertyName("prompt")] - public required string Prompt { get; set; } + /// Opaque routing identifier transmitted unchanged to the provider. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// Runtime model resolved for the task when available. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("resolvedModel")] - public string? ResolvedModel { get; set; } + /// Current account-specific availability. + [JsonPropertyName("status")] + public AutoTierStatus Status { get => field ??= new(); set; } - /// Result text from the task when available. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("result")] - public string? Result { get; set; } + /// Execution kind; this client supports `auto` preferences on the Auto model. + [JsonPropertyName("type")] + public string Type { get; set; } = string.Empty; +} - /// ISO 8601 timestamp when the task was started. - [JsonPropertyName("startedAt")] - public required DateTimeOffset StartedAt { get; set; } +/// Account-bound discovery metadata for the virtual `auto` model. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class AutoTierMetadata +{ + /// Provider-default preference, used only when no explicit preference exists. + [JsonPropertyName("defaultTier")] + public string DefaultTier { get; set; } = string.Empty; - /// Current lifecycle status of the task. - [JsonPropertyName("status")] - public required TaskStatus Status { get; set; } + /// Provider that supplied this metadata, when the catalog is provider-attributed. + [JsonPropertyName("providerId")] + public string? ProviderId { get; set; } - /// Tool call ID associated with this agent task. - [JsonPropertyName("toolCallId")] - public required string ToolCallId { get; set; } + /// Routing preferences in the server's presentation order. + [JsonPropertyName("tiers")] + public IList Tiers { get => field ??= []; set; } } -/// Public owner attribution for a client-owned task. Identifiers are opaque and never authorize requests. +/// Cost-category metadata for a CAPI model. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TaskClientOwner +public sealed class SessionModelPriceCategory { - /// ISO 8601 timestamp when the bound join disconnected. - [JsonPropertyName("disconnectedAt")] - public DateTimeOffset? DisconnectedAt { get; set; } + /// CAPI model identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// Display-only owner name. - [JsonPropertyName("displayName")] - public string? DisplayName { get; set; } + /// Cost category assigned to the model. + [JsonPropertyName("priceCategory")] + public ModelPickerPriceCategory PriceCategory { get; set; } +} - /// Opaque identity of the currently or most recently bound session join. - [JsonPropertyName("joinId")] - public string JoinId { get; set; } = string.Empty; +/// One model provider available to the session — the model analog of the account `ProviderDescriptor`. Opaque id/label/kind plus a stable ordering; central code never branches on kind. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ModelProviderDescriptor +{ + /// Opaque, stable provider id, stamped onto every model this provider returns. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// Class of the task owner. + /// The neutral provider kind. [JsonPropertyName("kind")] - public TaskClientOwnerKind Kind { get; set; } - - /// Opaque session-scoped participant identity. - [JsonPropertyName("participantId")] - public string ParticipantId { get; set; } = string.Empty; + public ModelProviderKind Kind { get; set; } - /// Whether this task's bound join is currently connected. - [JsonPropertyName("presence")] - public TaskClientOwnerPresence Presence { get; set; } + /// Human-readable menu label, owned by the runtime so every consumer renders identical text. + [JsonPropertyName("label")] + public string Label { get; set; } = string.Empty; - /// Display-only owner source. - [JsonPropertyName("source")] - public string? Source { get; set; } + /// Stable ordering key for presenting providers in a deterministic sequence. + [JsonPropertyName("ordering")] + public long Ordering { get; set; } } -/// Tracked client-owned task metadata. -/// The client variant of . +/// The list of models available to this session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskInfoClient : TaskInfo +public sealed class SessionModelList { - /// - [JsonIgnore] - public override string Type => "client"; + /// Ordered Auto routing preferences discovered for this session's account. + [JsonPropertyName("auto")] + public AutoTierMetadata? Auto { get; set; } - /// ISO 8601 timestamp when the current active segment started. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("activeStartedAt")] - public DateTimeOffset? ActiveStartedAt { get; set; } + /// Available models, ordered with the most preferred default first. Includes both Copilot (CAPI) models and any registry BYOK models; a BYOK model appears under its provider-qualified selection id (`provider/id`). + [JsonPropertyName("list")] + public IList List { get => field ??= []; set; } - /// Accumulated active execution time in milliseconds. - [JsonPropertyName("activeTimeMs")] - public required long ActiveTimeMs { get; set; } - - /// Whether the currently bound owner can receive a cancellation request. - [JsonPropertyName("canCancel")] - public required bool CanCancel { get; set; } - - /// Human-readable reason for terminal cancellation. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("cancellationReason")] - public string? CancellationReason { get; set; } - - /// Owner-scoped registration and reclaim key. - [JsonPropertyName("clientTaskId")] - public required string ClientTaskId { get; set; } - - /// ISO 8601 timestamp when the task reached a terminal status. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("completedAt")] - public DateTimeOffset? CompletedAt { get; set; } + /// Cost categories for the full CAPI catalog, including picker-disabled models that Auto may select. Metadata only; entries absent from `list` are not manually selectable. + [JsonPropertyName("modelPriceCategories")] + public IList? ModelPriceCategories { get; set; } - /// Task description. - [JsonPropertyName("description")] - public required string Description { get; set; } + /// The model providers available to this session, in ordering order, resolved from the account roster; empty when no provider is entitled (logged out / seatless). Each model in `list` carries its own provider reference; this roster gives the deterministic provider sequence and lets a consumer group by provider without deriving ordering from the model list. Central code never branches on a provider kind. + [JsonPropertyName("providers")] + public IList? Providers { get; set; } - /// Optional task display name. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("displayName")] - public string? DisplayName { get; set; } + /// Per-quota snapshots returned alongside the model list, keyed by quota type. + [JsonPropertyName("quotaSnapshots")] + public IDictionary? QuotaSnapshots { get; set; } +} - /// Human-readable terminal failure message. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("error")] - public string? Error { get; set; } +/// RPC data type for SessionModelList operations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionModelListRequest +{ + /// If true, bypasses the per-session model list cache and re-fetches from CAPI. + [JsonPropertyName("skipCache")] + public bool? SkipCache { get; set; } +} - /// Optional owner-supplied terminal failure code. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("errorCode")] - public string? ErrorCode { get; set; } +/// RPC data type for SessionModelListRequestWithSession operations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionModelListRequestWithSession +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Execution mode, which is always background for client-owned tasks. - [JsonPropertyName("executionMode")] - public required TaskClientExecutionMode ExecutionMode { get; set; } + /// If true, bypasses the per-session model list cache and re-fetches from CAPI. + [JsonPropertyName("skipCache")] + public bool? SkipCache { get; set; } +} - /// Canonical runtime-generated task identifier. - [JsonPropertyName("id")] - public required string Id { get; set; } +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionModeGetRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// ISO 8601 timestamp when the connected owner entered idle status. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("idleSince")] - public DateTimeOffset? IdleSince { get; set; } +/// Outcome of a session mode change, including any model switch it triggered and follow-up the host must perform. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ModeSetResult +{ + /// Whether the host should arm an interactive continuation after the mode change. + [JsonPropertyName("armInteractiveContinuation")] + public bool? ArmInteractiveContinuation { get; set; } - /// ISO 8601 timestamp of the most recent orphan transition. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("orphanedAt")] - public DateTimeOffset? OrphanedAt { get; set; } + /// Compaction confirmation required before the mode change can complete. + [JsonPropertyName("confirmation")] + public ModelSwitchConfirmation? Confirmation { get; set; } - /// Public attribution and presence for the task owner. - [JsonPropertyName("owner")] - public required TaskClientOwner Owner { get; set; } + /// Whether the host must defer implementing the requested mode change. + [JsonPropertyName("deferImplementation")] + public bool? DeferImplementation { get; set; } - /// ISO 8601 timestamp of the most recent successful reclaim. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("reclaimedAt")] - public DateTimeOffset? ReclaimedAt { get; set; } + /// Deprecation warnings associated with the model selected by the mode change. + [JsonPropertyName("deprecationWarnings")] + public IList? DeprecationWarnings { get; set; } - /// Opaque successful terminal result supplied by the task owner. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("result")] - public JsonElement? Result { get; set; } + /// User-facing outcome message for the model switch triggered by the mode change. + [JsonPropertyName("message")] + public string? Message { get; set; } - /// Sequence number of the latest accepted owner update. - [JsonPropertyName("sequence")] - public required long Sequence { get; set; } + /// Whether the requested mode was applied to the session. False only when an 'expectedMode' precondition did not hold, in which case any model change reported alongside it was still applied. + [JsonPropertyName("modeApplied")] + public bool? ModeApplied { get; set; } - /// ISO 8601 timestamp when the task started. - [JsonPropertyName("startedAt")] - public required DateTimeOffset StartedAt { get; set; } + /// Whether applying the mode changed the active model. + [JsonPropertyName("modelChanged")] + public bool ModelChanged { get; set; } - /// Client task lifecycle status. + /// Lifecycle status of the requested mode change. [JsonPropertyName("status")] - public required TaskClientStatus Status { get; set; } + public string Status { get; set; } = string.Empty; - /// ISO 8601 timestamp of the latest accepted lifecycle change. - [JsonPropertyName("updatedAt")] - public required DateTimeOffset UpdatedAt { get; set; } + /// User-facing warning produced while applying the mode change. + [JsonPropertyName("warning")] + public string? Warning { get; set; } } -/// Tracked shell task metadata, including ID, command, status, timing, attachment/execution mode, log path, and PID. -/// The shell variant of . +/// Agent interaction mode to apply to the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskInfoShell : TaskInfo +internal sealed class ModeSetRequest { - /// - [JsonIgnore] - public override string Type => "shell"; + /// Explicit response to a model-switch compaction preflight. + [JsonPropertyName("compactionDecision")] + public string? CompactionDecision { get; set; } - /// Whether the shell runs inside a managed PTY session or as an independent background process. - [JsonPropertyName("attachmentMode")] - public required TaskShellInfoAttachmentMode AttachmentMode { get; set; } + /// Mode the session must currently be in for the change to apply. When set and the session is in a different mode the request is a no-op and reports status 'unchanged'. + [JsonPropertyName("expectedMode")] + public SessionMode? ExpectedMode { get; set; } - /// Whether this shell task can be promoted to background mode. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("canPromoteToBackground")] - public bool? CanPromoteToBackground { get; set; } + /// Session whose plan-mode base state should be inherited. + [JsonPropertyName("inheritPlanBaseFromSessionId")] + public string? InheritPlanBaseFromSessionId { get; set; } - /// Command being executed. - [JsonPropertyName("command")] - public required string Command { get; set; } + /// The session mode the agent is operating in. + [JsonPropertyName("mode")] + public SessionMode Mode { get; set; } - /// ISO 8601 timestamp when the task finished. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("completedAt")] - public DateTimeOffset? CompletedAt { get; set; } + /// Whether the selected plan model should be persisted. + [JsonPropertyName("persistPlanSelection")] + public bool? PersistPlanSelection { get; set; } - /// Short description of the task. - [JsonPropertyName("description")] - public required string Description { get; set; } + /// Settings context used when persisting the selected plan model. + [JsonPropertyName("pickerSettingsContext")] + public ModelPickerSettingsContext? PickerSettingsContext { get; set; } - /// Whether task execution is synchronously awaited or managed in the background. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("executionMode")] - public TaskExecutionMode? ExecutionMode { get; set; } + /// Context tier to use with the dedicated plan model. + [JsonPropertyName("planContextTier")] + public string? PlanContextTier { get; set; } - /// Unique task identifier. - [JsonPropertyName("id")] - public required string Id { get; set; } + /// Action to perform when leaving plan mode. + [JsonPropertyName("planExitAction")] + public string? PlanExitAction { get; set; } - /// Path to the detached shell log, when available. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("logPath")] - public string? LogPath { get; set; } + /// Dedicated model to use in plan mode, when configured. + [JsonPropertyName("planModel")] + public string? PlanModel { get; set; } - /// Process ID when available. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("pid")] - public long? Pid { get; set; } + /// Whether a dedicated plan model is configured. + [JsonPropertyName("planModelConfigured")] + public bool? PlanModelConfigured { get; set; } - /// ISO 8601 timestamp when the task was started. - [JsonPropertyName("startedAt")] - public required DateTimeOffset StartedAt { get; set; } + /// Provider owning planModel. Omit for legacy bare-model selection. + [JsonPropertyName("planModelProviderId")] + public string? PlanModelProviderId { get; set; } - /// Current lifecycle status of the task. - [JsonPropertyName("status")] - public required TaskStatus Status { get; set; } + /// Reasoning effort to use with the dedicated plan model. + [JsonPropertyName("planReasoningEffort")] + public string? PlanReasoningEffort { get; set; } + + /// Whether leaving plan mode should restore the session's previous model. + [JsonPropertyName("restorePlanModel")] + public bool? RestorePlanModel { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Background tasks currently tracked by the session. +/// The session's friendly name, or null when not yet set. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TaskList +public sealed class NameGetResult { - /// Currently tracked tasks. - [JsonPropertyName("tasks")] - public IList Tasks { get => field ??= []; set; } + /// The session name (user-set or auto-generated), or null if not yet set. + [JsonPropertyName("name")] + public string? Name { get; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionTasksListRequest +internal sealed class SessionNameGetRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Tracked client-owned task metadata. +/// New friendly name to apply to the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TaskClientInfo +internal sealed class NameSetRequest { - /// ISO 8601 timestamp when the current active segment started. - [JsonPropertyName("activeStartedAt")] - public DateTimeOffset? ActiveStartedAt { get; set; } - - /// Accumulated active execution time in milliseconds. - [JsonPropertyName("activeTimeMs")] - public long ActiveTimeMs { get; set; } - - /// Whether the currently bound owner can receive a cancellation request. - [JsonPropertyName("canCancel")] - public bool CanCancel { get; set; } - - /// Human-readable reason for terminal cancellation. - [JsonPropertyName("cancellationReason")] - public string? CancellationReason { get; set; } - - /// Owner-scoped registration and reclaim key. - [JsonPropertyName("clientTaskId")] - public string ClientTaskId { get; set; } = string.Empty; + /// New session name (1–100 characters, trimmed of leading/trailing whitespace). + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(100)] + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// ISO 8601 timestamp when the task reached a terminal status. - [JsonPropertyName("completedAt")] - public DateTimeOffset? CompletedAt { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Task description. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; +/// Indicates whether the auto-generated summary was applied as the session's name. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class NameSetAutoResult +{ + /// Whether the auto-generated summary was persisted. False if the session already has a user-set name, the summary normalized to empty, or the session does not have a workspace. + [JsonPropertyName("applied")] + public bool Applied { get; set; } +} - /// Optional task display name. - [JsonPropertyName("displayName")] - public string? DisplayName { get; set; } +/// Auto-generated session summary to apply as the session's name when no user-set name exists. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class NameSetAutoRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Human-readable terminal failure message. - [JsonPropertyName("error")] - public string? Error { get; set; } + /// Auto-generated session summary. Empty/whitespace-only values are ignored; values are trimmed before persisting. + [JsonPropertyName("summary")] + public string Summary { get; set; } = string.Empty; +} - /// Optional owner-supplied terminal failure code. - [JsonPropertyName("errorCode")] - public string? ErrorCode { get; set; } +/// Existence, contents, and resolved path of the session plan file. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PlanReadResult +{ + /// The content of the plan file, or null if it does not exist. + [JsonPropertyName("content")] + public string? Content { get; set; } - /// Execution mode, which is always background for client-owned tasks. - [JsonPropertyName("executionMode")] - public TaskClientExecutionMode ExecutionMode { get; set; } + /// Whether the plan file exists in the workspace. + [JsonPropertyName("exists")] + public bool Exists { get; set; } - /// Canonical runtime-generated task identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Absolute file path of the plan file, or null if workspace is not enabled. + [JsonPropertyName("path")] + public string? Path { get; set; } +} - /// ISO 8601 timestamp when the connected owner entered idle status. - [JsonPropertyName("idleSince")] - public DateTimeOffset? IdleSince { get; set; } +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionPlanReadRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// ISO 8601 timestamp of the most recent orphan transition. - [JsonPropertyName("orphanedAt")] - public DateTimeOffset? OrphanedAt { get; set; } +/// Replacement contents to write to the session plan file. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class PlanUpdateRequest +{ + /// The new content for the plan file. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; - /// Public attribution and presence for the task owner. - [JsonPropertyName("owner")] - public TaskClientOwner Owner { get => field ??= new(); set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// ISO 8601 timestamp of the most recent successful reclaim. - [JsonPropertyName("reclaimedAt")] - public DateTimeOffset? ReclaimedAt { get; set; } +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionPlanDeleteRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Opaque successful terminal result supplied by the task owner. - [JsonPropertyName("result")] - public JsonElement? Result { get; set; } +/// A single todo row read from the session SQL `todos` table. All fields are optional because the SQL schema is best-effort and the agent may not have populated every column. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PlanSqlTodosRow +{ + /// Todo creation time, as stored by the session SQL schema's `datetime('now')` default: `YYYY-MM-DD HH:MM:SS` in UTC. Lets clients attribute todos to the work item that created them (e.g. scoping a goal's progress to the todos it produced) rather than to the whole session. + [JsonPropertyName("createdAt")] + public string? CreatedAt { get; set; } - /// Sequence number of the latest accepted owner update. - [JsonPropertyName("sequence")] - public long Sequence { get; set; } + /// Todo description. + [JsonPropertyName("description")] + public string? Description { get; set; } - /// ISO 8601 timestamp when the task started. - [JsonPropertyName("startedAt")] - public DateTimeOffset StartedAt { get; set; } + /// Todo identifier. + [JsonPropertyName("id")] + public string? Id { get; set; } - /// Client task lifecycle status. + /// Todo status. [JsonPropertyName("status")] - public TaskClientStatus Status { get; set; } - - /// Task kind. - [JsonPropertyName("type")] - public TaskClientType Type { get; set; } + public string? Status { get; set; } - /// ISO 8601 timestamp of the latest accepted lifecycle change. - [JsonPropertyName("updatedAt")] - public DateTimeOffset UpdatedAt { get; set; } + /// Todo title. + [JsonPropertyName("title")] + public string? Title { get; set; } } -/// Result of registering or reclaiming a client-owned task. +/// Todo rows read from the session SQL database. Empty when no session database is available. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksRegisterResult +public sealed class PlanReadSqlTodosResult { - /// True only when this invocation created a new task. - [JsonPropertyName("created")] - public bool Created { get; set; } - - /// True only when this invocation reclaimed an orphaned task. - [JsonPropertyName("reclaimed")] - public bool Reclaimed { get; set; } - - /// Authoritative registered or reclaimed task. - [JsonPropertyName("task")] - public TaskClientInfo Task { get => field ??= new(); set; } + /// Rows from the session SQL todos table, ordered by creation time with insertion order used to break ties when available and id used for WITHOUT ROWID tables. + [JsonPropertyName("rows")] + public IList Rows { get => field ??= []; set; } } -/// Registers or reclaims a client-owned task. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksRegisterRequest +internal sealed class SessionPlanReadSqlTodosRequest { - /// Whether the owner supports runtime cancellation requests. - [JsonPropertyName("cancellable")] - public bool Cancellable { get; set; } - - /// Owner-scoped idempotency key used for registration and reclaim. - [JsonPropertyName("clientTaskId")] - public string ClientTaskId { get; set; } = string.Empty; - - /// Human-readable description of the external work. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; - - /// Optional short display name for the external work. - [JsonPropertyName("displayName")] - public string? DisplayName { get; set; } - - /// Expected current sequence for idempotent registration or orphan reclaim. - [JsonPropertyName("expectedSequence")] - public long? ExpectedSequence { get; set; } - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// Task kind. - [JsonPropertyName("type")] - public TaskClientType Type { get; set; } } -/// Result of publishing a client-owned task update. +/// A single dependency edge read from the session SQL `todo_deps` table, indicating that one todo must complete before another. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksUpdateResult +public sealed class PlanSqlTodoDependency { - /// Whether this invocation changed task state. - [JsonPropertyName("applied")] - public bool Applied { get; set; } - - /// Whether this invocation repeated the latest accepted update. - [JsonPropertyName("duplicate")] - public bool Duplicate { get; set; } + /// ID of the todo it depends on. + [JsonPropertyName("dependsOn")] + public string DependsOn { get; set; } = string.Empty; - /// Authoritative task after processing the update. - [JsonPropertyName("task")] - public TaskClientInfo Task { get => field ??= new(); set; } + /// ID of the todo that has the dependency. + [JsonPropertyName("todoId")] + public string TodoId { get; set; } = string.Empty; } -/// Progress or terminal update for a client-owned task. -/// Polymorphic base type discriminated by kind. +/// Todo rows + dependency edges read from the session SQL database. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(TaskClientUpdateProgress), "progress")] -[JsonDerivedType(typeof(TaskClientUpdateCompleted), "completed")] -[JsonDerivedType(typeof(TaskClientUpdateFailed), "failed")] -[JsonDerivedType(typeof(TaskClientUpdateCancelled), "cancelled")] -public partial class TaskClientUpdate +public sealed class PlanReadSqlTodosWithDependenciesResult { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// Edges from the session SQL todo_deps table. Empty when no database, no todo_deps table, or the SELECT failed. Read independently from `rows`, so a broken todo_deps table does not affect the rows result and vice versa. + [JsonPropertyName("dependencies")] + public IList Dependencies { get => field ??= []; set; } + /// Rows from the session SQL todos table, ordered by creation time with insertion order used to break ties when available and id used for WITHOUT ROWID tables. Empty when no database, no todos table, or the SELECT failed. + [JsonPropertyName("rows")] + public IList Rows { get => field ??= []; set; } +} -/// Publishes nonterminal progress for a running or idle client task. -/// The progress variant of . +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskClientUpdateProgress : TaskClientUpdate +internal sealed class SessionPlanReadSqlTodosWithDependenciesRequest { - /// - [JsonIgnore] - public override string Kind => "progress"; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Optional progress message appended to recent activity when nonempty. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("message")] - public string? Message { get; set; } +/// RPC data type for WorkspacesGetWorkspaceResultWorkspace operations. +public sealed class WorkspacesGetWorkspaceResultWorkspace +{ + /// Current Git branch. + [JsonPropertyName("branch")] + public string? Branch { get; set; } - /// Optional completion percentage; null clears the current percentage. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("percentage")] - public double? Percentage { get; set; } + /// Whether the per-session Chronicle upgrade prompt was dismissed for the workspace. + [JsonPropertyName("chronicle_sync_dismissed")] + public bool? ChronicleSyncDismissed { get; set; } - /// Optional progress phase; null clears the current phase. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("phase")] - public string? Phase { get; set; } + /// Name of the client that created the workspace. + [JsonPropertyName("client_name")] + public string? ClientName { get; set; } - /// Optional active status transition. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("status")] - public TaskClientActiveStatus? Status { get; set; } -} + /// Timestamp when the workspace was created. + [JsonPropertyName("created_at")] + public DateTimeOffset? CreatedAt { get; set; } -/// Reports successful terminal completion. -/// The completed variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskClientUpdateCompleted : TaskClientUpdate -{ - /// - [JsonIgnore] - public override string Kind => "completed"; + /// Current working directory associated with the workspace. + [JsonPropertyName("cwd")] + public string? Cwd { get; set; } - /// Optional final progress message. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("message")] - public string? Message { get; set; } + /// Git repository root associated with the workspace. + [JsonPropertyName("git_root")] + public string? GitRoot { get; set; } - /// Optional opaque successful terminal result. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("result")] - public JsonElement? Result { get; set; } -} + /// Allowed values for the `WorkspacesWorkspaceDetailsHostType` enumeration. + [JsonPropertyName("host_type")] + public WorkspacesWorkspaceDetailsHostType? HostType { get; set; } -/// Reports terminal failure. -/// The failed variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskClientUpdateFailed : TaskClientUpdate -{ - /// - [JsonIgnore] - public override string Kind => "failed"; + /// Stable workspace identifier. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// Optional owner-supplied terminal failure code. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("code")] - public string? Code { get; set; } + /// Most recent Mission Control event identifier observed for the workspace. + [JsonPropertyName("mc_last_event_id")] + public string? McLastEventId { get; set; } - /// Human-readable terminal failure message. - [JsonPropertyName("error")] - public required string Error { get; set; } + /// Mission Control session identifier associated with the workspace. + [JsonPropertyName("mc_session_id")] + public string? McSessionId { get; set; } - /// Optional final progress message. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("message")] - public string? Message { get; set; } -} + /// Mission Control task identifier associated with the workspace. + [JsonPropertyName("mc_task_id")] + public string? McTaskId { get; set; } -/// Reports terminal cancellation after external work stopped. -/// The cancelled variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskClientUpdateCancelled : TaskClientUpdate -{ - /// - [JsonIgnore] - public override string Kind => "cancelled"; + /// Workspace display name. + [JsonPropertyName("name")] + public string? Name { get; set; } - /// Optional final progress message. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("message")] - public string? Message { get; set; } + /// Whether the workspace session can be steered remotely. + [JsonPropertyName("remote_steerable")] + public bool? RemoteSteerable { get; set; } - /// Optional human-readable cancellation reason. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("reason")] - public string? Reason { get; set; } + /// Repository identifier associated with the workspace. + [JsonPropertyName("repository")] + public string? Repository { get; set; } + + /// Number of persisted summaries in the workspace. + [JsonPropertyName("summary_count")] + public long? SummaryCount { get; set; } + + /// Timestamp when the workspace was last updated. + [JsonPropertyName("updated_at")] + public DateTimeOffset? UpdatedAt { get; set; } + + /// Whether the workspace name was explicitly chosen by the user. + [JsonPropertyName("user_named")] + public bool? UserNamed { get; set; } } -/// Updates a client-owned task. +/// Current workspace metadata for the session, including its absolute filesystem path when available. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksUpdateRequest +public sealed class WorkspacesGetWorkspaceResult { - /// Canonical runtime-generated task identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Absolute filesystem path to the workspace directory. Omitted when the session has no workspace (e.g. remote sessions). + [JsonPropertyName("path")] + public string? Path { get; set; } - /// Owner update sequence to apply. - [JsonPropertyName("sequence")] - public long Sequence { get; set; } + /// Current workspace metadata, or null if not available. + [JsonPropertyName("workspace")] + public WorkspacesGetWorkspaceResultWorkspace? Workspace { get; set; } +} +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionWorkspacesGetWorkspaceRequest +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// Progress or terminal update payload. - [JsonPropertyName("update")] - public TaskClientUpdate Update { get => field ??= new(); set; } } -/// 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. +/// Workspace metadata fields to update. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksRefreshResult +internal sealed class WorkspacesUpdateMetadataRequest { + /// Opaque workspace context supplied by the session host. + [JsonPropertyName("context")] + public JsonElement? Context { get; set; } + + /// Optional workspace display name override. + [JsonPropertyName("name")] + public string? Name { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Identifies the target session. +/// Optional session context used when creating a local workspace. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionTasksRefreshRequest +internal sealed class WorkspacesEnsureRequest { + /// Opaque workspace context supplied by the session host. + [JsonPropertyName("context")] + public JsonElement? Context { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// 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). +/// Relative paths of files stored in the session workspace files directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksWaitForPendingResult +public sealed class WorkspacesListFilesResult { + /// Slash-separated relative file paths in the workspace files directory. + [JsonPropertyName("files")] + public IList Files { get => field ??= []; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionTasksWaitForPendingRequest +internal sealed class SessionWorkspacesListFilesRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Progress information for the task, discriminated by type. Returns null when no task with this ID is currently tracked. -/// Polymorphic base type discriminated by type. +/// Contents of the requested workspace file as a UTF-8 string. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "type", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(TaskProgressAgent), "agent")] -[JsonDerivedType(typeof(TaskProgressClient), "client")] -[JsonDerivedType(typeof(TaskProgressShell), "shell")] -public partial class TaskProgress +public sealed class WorkspacesReadFileResult { - /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { get; set; } = string.Empty; + /// File content as a UTF-8 string. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; } - -/// Timestamped display line for task progress output or recent agent activity. +/// Relative path of the workspace file to read. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TaskProgressLine +internal sealed class WorkspacesReadFileRequest { - /// Display message, e.g., "▸ bash", "✓ edit src/foo.ts". - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; + /// Slash-separated relative path within the workspace files directory. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; - /// ISO 8601 timestamp when this event occurred. - [JsonPropertyName("timestamp")] - public DateTimeOffset Timestamp { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Progress snapshot for an agent task, with recent activity lines and optional latest intent. -/// The agent variant of . +/// Relative path and UTF-8 content for the workspace file to create or overwrite. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskProgressAgent : TaskProgress +internal sealed class WorkspacesCreateFileRequest { - /// - [JsonIgnore] - public override string Type => "agent"; + /// File content to write as a UTF-8 string. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; - /// The most recent intent reported by the agent. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("latestIntent")] - public string? LatestIntent { get; set; } + /// Slash-separated relative path within the workspace files directory. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; - /// Recent tool execution events converted to display lines. - [JsonPropertyName("recentActivity")] - public required IList RecentActivity { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Generic progress for a client-owned task. -/// The client variant of . +/// Filesystem metadata for a path in the session workspace files directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskProgressClient : TaskProgress +public sealed class WorkspacesStatFileResult { - /// - [JsonIgnore] - public override string Type => "client"; - - /// Most recent nonempty progress message. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("lastMessage")] - public string? LastMessage { get; set; } + /// Creation time in Unix epoch milliseconds. + [JsonPropertyName("birthtimeMs")] + public double BirthtimeMs { get; set; } - /// Current completion percentage from zero through one hundred. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("percentage")] - public double? Percentage { get; set; } + /// Whether the path identifies a directory. + [JsonPropertyName("isDirectory")] + public bool IsDirectory { get; set; } - /// Current owner-defined progress phase. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("phase")] - public string? Phase { get; set; } + /// Whether the path identifies a regular file. + [JsonPropertyName("isFile")] + public bool IsFile { get; set; } - /// Recent server-timestamped progress messages. - [JsonPropertyName("recentActivity")] - public required IList RecentActivity { get; set; } + /// Last modification time in Unix epoch milliseconds. + [JsonPropertyName("mtimeMs")] + public double MtimeMs { get; set; } - /// Sequence number of the latest accepted owner update. - [JsonPropertyName("sequence")] - public required long Sequence { get; set; } + /// Size in bytes. + [JsonPropertyName("size")] + public double Size { get; set; } +} - /// Current client task lifecycle status. - [JsonPropertyName("status")] - public required TaskClientStatus Status { get; set; } +/// Relative path of the workspace file or directory to inspect. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class WorkspacesStatFileRequest +{ + /// Slash-separated relative path within the workspace files directory. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; - /// ISO 8601 timestamp of the latest accepted lifecycle change. - [JsonPropertyName("updatedAt")] - public required DateTimeOffset UpdatedAt { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Progress snapshot for a shell task, with recent stdout/stderr output and optional process ID. -/// The shell variant of . +/// Directory to create within the session workspace files directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskProgressShell : TaskProgress +internal sealed class WorkspacesCreateDirectoryRequest { - /// - [JsonIgnore] - public override string Type => "shell"; + /// Slash-separated relative path within the workspace files directory. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; - /// Process ID when available. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("pid")] - public long? Pid { get; set; } + /// Whether to create missing parent directories. Defaults to false. + [JsonPropertyName("recursive")] + public bool? Recursive { get; set; } - /// Recent stdout/stderr lines from the running shell command. - [JsonPropertyName("recentOutput")] - public required string RecentOutput { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Progress information for the task, or null when no task with that ID is tracked. +/// File or directory to remove from the session workspace files directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksGetProgressResult +internal sealed class WorkspacesRemovePathRequest { - /// Progress information for the task, discriminated by type. Returns null when no task with this ID is currently tracked. - [JsonPropertyName("progress")] - public TaskProgress? Progress { get; set; } + /// Whether a missing path should be treated as success. Defaults to false. + [JsonPropertyName("force")] + public bool? Force { get; set; } + + /// Slash-separated relative path within the workspace files directory. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + + /// Whether to remove directory contents recursively. Defaults to false. + [JsonPropertyName("recursive")] + public bool? Recursive { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Identifier of the background task to fetch progress for. +/// Source and destination paths for a rename within the session workspace files directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksGetProgressRequest +internal sealed class WorkspacesRenamePathRequest { - /// Task identifier (agent ID or shell ID). - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Slash-separated destination path relative to the workspace files directory. + [JsonPropertyName("destination")] + public string Destination { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Slash-separated source path relative to the workspace files directory. + [JsonPropertyName("source")] + public string Source { get; set; } = string.Empty; } -/// The first sync-waiting task that can currently be promoted to background mode. +/// Workspace checkpoint metadata with assigned number, human-readable title, and checkpoint filename. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksGetCurrentPromotableResult +public sealed class WorkspacesCheckpoints { - /// The first sync-waiting task (agent first, then shell) that can currently be promoted to background mode. Omitted if no such task exists. The returned task is guaranteed to have executionMode='sync' and canPromoteToBackground=true at the time of the call. - [JsonPropertyName("task")] - public TaskInfo? Task { get; set; } + /// Filename of the checkpoint within the workspace checkpoints directory. + [JsonPropertyName("filename")] + public string Filename { get; set; } = string.Empty; + + /// Checkpoint number assigned by the workspace manager. + [JsonPropertyName("number")] + public long Number { get; set; } + + /// Human-readable checkpoint title. + [JsonPropertyName("title")] + public string Title { get; set; } = string.Empty; +} + +/// Workspace checkpoints in chronological order; empty when the workspace is not enabled. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class WorkspacesListCheckpointsResult +{ + /// Workspace checkpoints in chronological order. Empty when workspace is not enabled. + [JsonPropertyName("checkpoints")] + public IList Checkpoints { get => field ??= []; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionTasksGetCurrentPromotableRequest +internal sealed class SessionWorkspacesListCheckpointsRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the task was successfully promoted to background mode. +/// Checkpoint content as a UTF-8 string, or null when the checkpoint or workspace is missing. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksPromoteToBackgroundResult +public sealed class WorkspacesReadCheckpointResult { - /// Whether the task was successfully promoted to background mode. - [JsonPropertyName("promoted")] - public bool Promoted { get; set; } + /// Checkpoint content as a UTF-8 string, or null when the checkpoint or workspace is missing. + [JsonPropertyName("content")] + public string? Content { get; set; } } -/// Identifier of the task to promote to background mode. +/// Checkpoint number to read. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksPromoteToBackgroundRequest +internal sealed class WorkspacesReadCheckpointRequest { - /// Task identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Checkpoint number to read. + [JsonPropertyName("number")] + public long Number { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// The promoted task as it now exists in background mode, omitted if no promotable task was waiting. +/// Persisted summary metadata and refreshed workspace metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksPromoteCurrentToBackgroundResult +public sealed class WorkspacesAddSummaryResult { - /// The promoted task as it now exists in background mode, omitted if no promotable task was waiting. Atomic operation: avoids the race window of getCurrentPromotable + promoteToBackground. - [JsonPropertyName("task")] - public TaskInfo? Task { get; set; } + /// Metadata for the persisted summary. + [JsonPropertyName("summary")] + public IDictionary? Summary { get; set; } + + /// Refreshed metadata for the containing workspace. + [JsonPropertyName("workspace")] + public IDictionary? Workspace { get; set; } } -/// Identifies the target session. +/// Compaction summary checkpoint to persist. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionTasksPromoteCurrentToBackgroundRequest +internal sealed class WorkspacesAddSummaryRequest { + /// Markdown summary content to persist. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Summary title shown in checkpoint listings. + [JsonPropertyName("title")] + public string Title { get; set; } = string.Empty; } -/// Indicates whether the background task was successfully cancelled. +/// Rollback point for local workspace summaries. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksCancelResult +internal sealed class WorkspacesTruncateSummariesRequest { - /// Whether the task was successfully cancelled. - [JsonPropertyName("cancelled")] - public bool Cancelled { get; set; } + /// Number of newest summaries to keep. + [JsonPropertyName("keepCount")] + public long KeepCount { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Identifier of the background task to cancel. +/// Autopilot objective file content, or null when missing. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksCancelRequest +public sealed class WorkspacesReadAutopilotObjectiveResult { - /// Task identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Autopilot objective file content, or null when missing. + [JsonPropertyName("content")] + public string? Content { get; set; } +} +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionWorkspacesReadAutopilotObjectiveRequest +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the task was removed. False when the task does not exist or is still running/idle. +/// Result of writing the autopilot objective file. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksRemoveResult +public sealed class WorkspacesWriteAutopilotObjectiveResult { - /// Whether the task was removed. Returns false if the task does not exist or is still running/idle (cancel it first). - [JsonPropertyName("removed")] - public bool Removed { get; set; } + /// Filesystem operation performed. + [JsonPropertyName("operation")] + public string Operation { get; set; } = string.Empty; } -/// Identifier of the completed or cancelled task to remove from tracking. +/// Autopilot objective file content to persist. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksRemoveRequest +internal sealed class WorkspacesWriteAutopilotObjectiveRequest { - /// Task identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Autopilot objective file content. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the message was delivered, with an error message when delivery failed. +/// Result of deleting the autopilot objective file. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksSendMessageResult +public sealed class WorkspacesDeleteAutopilotObjectiveResult { - /// Error message if delivery failed. - [JsonPropertyName("error")] - public string? Error { get; set; } - - /// Whether the message was successfully delivered or steered. - [JsonPropertyName("sent")] - public bool Sent { get; set; } + /// True when a file was deleted. + [JsonPropertyName("deleted")] + public bool Deleted { get; set; } } -/// Identifier of the target agent task, message content, and optional sender agent ID. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksSendMessageRequest +internal sealed class SessionWorkspacesDeleteAutopilotObjectiveRequest { - /// Agent ID of the sender, if sent on behalf of another agent. - [JsonPropertyName("fromAgentId")] - public string? FromAgentId { get; set; } - - /// Agent task identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; - - /// Message content to send to the agent. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Skill metadata available to a session, with name, description, source, enabled/invocable state, path, plugin, and argument hint. +/// Whether the autopilot objective file exists. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class Skill +public sealed class WorkspacesAutopilotObjectiveExistsResult { - /// Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field. - [JsonPropertyName("argumentHint")] - public string? ArgumentHint { get; set; } - - /// Canonical slash command name used to invoke the skill, without the leading '/'. - [JsonPropertyName("commandName")] - public string? CommandName { get; set; } - - /// Description of what the skill does. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; - - /// Whether the skill is currently enabled. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } - - /// Unique identifier for the skill. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// True when the objective file exists. + [JsonPropertyName("exists")] + public bool Exists { get; set; } +} - /// Absolute path to the skill file. - [JsonPropertyName("path")] - public string? Path { get; set; } +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionWorkspacesAutopilotObjectiveExistsRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Name of the plugin that provides the skill, when source is 'plugin'. - [JsonPropertyName("pluginName")] - public string? PluginName { get; set; } +/// RPC data type for WorkspacesSaveLargePasteResultSaved operations. +public sealed class WorkspacesSaveLargePasteResultSaved +{ + /// Filename within the workspace files directory. + [JsonPropertyName("filename")] + public string Filename { get; set; } = string.Empty; - /// Source location type (e.g., project, personal-copilot, plugin, builtin). - [JsonPropertyName("source")] - public SkillSource Source { get; set; } + /// Absolute filesystem path to the saved paste file. + [JsonPropertyName("filePath")] + public string FilePath { get; set; } = string.Empty; - /// Whether the skill can be invoked by the user as a slash command. - [JsonPropertyName("userInvocable")] - public bool UserInvocable { get; set; } + /// Size of the saved file in bytes. + [JsonPropertyName("sizeBytes")] + public long SizeBytes { get; set; } } -/// Skills available to the session, with their enabled state. +/// Descriptor for the saved paste file, or null when the workspace is unavailable. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillList +public sealed class WorkspacesSaveLargePasteResult { - /// Available skills. - [JsonPropertyName("skills")] - public IList Skills { get => field ??= []; set; } + /// Saved-paste descriptor, or null when the workspace is unavailable (e.g. CCA runtime, non-infinite sessions, remote sessions). + [JsonPropertyName("saved")] + public WorkspacesSaveLargePasteResultSaved? Saved { get; set; } } -/// Identifies the target session. +/// Pasted content to save as a UTF-8 file in the session workspace. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSkillsListRequest +internal sealed class WorkspacesSaveLargePasteRequest { + /// Pasted content to save as a UTF-8 file. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Skill invocation record with name, path, content, allowed tools, and turn number. +/// Complete text used to generate one session diff. These are display contents, with the same text decoding as the patch, not a file-restore contract. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillsInvokedSkill +public sealed class WorkspaceDiffContents { - /// Tools that should be auto-approved when this skill is active, captured at invocation time. - [JsonPropertyName("allowedTools")] - public IList? AllowedTools { get; set; } + /// Complete current text read when computing the diff. Omitted for a deleted file; an empty string represents an existing empty file. + [JsonPropertyName("after")] + public string? After { get; set; } - /// Full content of the skill file. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// Complete text before the session first changed the file. Omitted when the file did not exist; an empty string represents an existing empty file. + [JsonPropertyName("before")] + public string? Before { get; set; } +} - /// Whether model invocation was disabled when this skill was invoked. - [JsonPropertyName("disableModelInvocation")] - public bool? DisableModelInvocation { get; set; } +/// A single changed file and its unified diff. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class WorkspaceDiffFileChange +{ + /// Type of change represented by this file diff. + [JsonPropertyName("changeType")] + public WorkspaceDiffFileChangeType ChangeType { get; set; } - /// Turn number when the skill was invoked. - [JsonPropertyName("invokedAtTurn")] - public long InvokedAtTurn { get; set; } + /// Full text used for this patch, only when includeContents was requested for session mode. Omitted for binary, oversized or unavailable contents, and for fallback results. Read isFallback and isTruncated before treating an absent value as a missing file. + [JsonPropertyName("contents")] + public WorkspaceDiffContents? Contents { get; set; } - /// Unique identifier for the skill. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Unified diff content for the file. Empty when the diff was truncated. + [JsonPropertyName("diff")] + public string Diff { get; set; } = string.Empty; - /// Path to the SKILL.md file, or an empty string for an SDK-provided skill without a filesystem identity. + /// Whether the diff content was omitted because it exceeded the per-file size limit. + [JsonPropertyName("isTruncated")] + public bool? IsTruncated { get; set; } + + /// Original file path for renamed files. + [JsonPropertyName("oldPath")] + public string? OldPath { get; set; } + + /// Path to the changed file, relative to the workspace root when the file lives under it. A file changed outside the workspace root keeps a `../`-relative path, or an absolute path when no relative path exists (for example a different Windows drive). [JsonPropertyName("path")] public string Path { get; set; } = string.Empty; } -/// Skills invoked during this session, ordered by invocation time (most recent last). +/// Workspace diff result for the requested mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillsGetInvokedResult +public sealed class WorkspaceDiffResult { - /// Skills invoked during this session, ordered by invocation time (most recent last). - [JsonPropertyName("skills")] - public IList Skills { get => field ??= []; set; } + /// Default branch used for a branch diff, when branch mode was requested. + [JsonPropertyName("baseBranch")] + public string? BaseBranch { get; set; } + + /// Changed files and their unified diffs. + [JsonPropertyName("changes")] + public IList Changes { get => field ??= []; set; } + + /// Whether the requested diff fell back to unstaged changes, either because branch diff failed or session diff was unavailable. + [JsonPropertyName("isFallback")] + public bool IsFallback { get; set; } + + /// Effective mode used for the returned changes. + [JsonPropertyName("mode")] + public WorkspaceDiffMode Mode { get; set; } + + /// Diff mode requested by the client. + [JsonPropertyName("requestedMode")] + public WorkspaceDiffMode RequestedMode { get; set; } + + /// Why the session diff could not be produced, when applicable. Set only when `session` mode was requested and `isFallback` is true, so a client can tell the permanent `file-change-tracking-disabled` apart from the transient `session-busy`, which the same request answers once the session settles. Never set for `unstaged` or `branch` mode, and never `unsupported-remote-session`: a remote session's captures live on its own host, so a `session`-mode diff is rejected for one rather than answered with a controller-side fallback. + [JsonPropertyName("unavailableReason")] + public HistoryRewindUnavailableReason? UnavailableReason { get; set; } } -/// Identifies the target session. +/// Parameters for computing a workspace diff. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSkillsGetInvokedRequest +public sealed class WorkspacesDiffRequest { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// When true, ignore whitespace-only changes (git `--ignore-all-space`). Defaults to false. + [JsonPropertyName("ignoreWhitespace")] + public bool? IgnoreWhitespace { get; set; } + + /// Include the full before/after text used to compute each session diff. Defaults to false; true is accepted only for session mode. Existing capture/read limits still apply, and binary or unavailable contents are not returned. This can substantially increase response size. + [JsonPropertyName("includeContents")] + public bool? IncludeContents { get; set; } + + /// Diff mode requested by the client. + [JsonPropertyName("mode")] + public required WorkspaceDiffMode Mode { get; set; } } -/// Name of the skill to enable for the session. +/// Parameters for computing a workspace diff. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SkillsEnableRequest +internal sealed class WorkspacesDiffRequestWithSession { - /// Name of the skill to enable. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// When true, ignore whitespace-only changes (git `--ignore-all-space`). Defaults to false. + [JsonPropertyName("ignoreWhitespace")] + public bool? IgnoreWhitespace { get; set; } + + /// Include the full before/after text used to compute each session diff. Defaults to false; true is accepted only for session mode. Existing capture/read limits still apply, and binary or unavailable contents are not returned. This can substantially increase response size. + [JsonPropertyName("includeContents")] + public bool? IncludeContents { get; set; } + + /// Diff mode requested by the client. + [JsonPropertyName("mode")] + public WorkspaceDiffMode Mode { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Name of the skill to disable for the session. +/// Current per-window credit limit and consumption for an autopilot objective. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SkillsDisableRequest +public sealed class AutopilotObjectiveCreditLimit { - /// Name of the skill to disable. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Configured AI-credit cap, when one is set. + [JsonPropertyName("credits")] + public double? Credits { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Window consumption in fractional AI credits, for display. + [JsonPropertyName("creditsUsed")] + public double CreditsUsed { get; set; } + + /// Exact window consumption in non-negative integer nano-AIU, encoded as a decimal string. + [RegularExpression("^[0-9]+$")] + [JsonPropertyName("creditsUsedNanoAiu")] + public string CreditsUsedNanoAiu { get; set; } = string.Empty; } -/// Diagnostics from reloading skill definitions, with warnings and errors as separate lists. +/// Public, persistence-independent projection of an autopilot objective. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillsLoadDiagnostics +public sealed class AutopilotObjectiveState { - /// Errors emitted while loading skills (e.g. skills that failed to load entirely). - [JsonPropertyName("errors")] - public IList Errors { get => field ??= []; set; } + /// Optional summary recorded when the objective completed. + [JsonPropertyName("completionSummary")] + public string? CompletionSummary { get; set; } - /// Warnings emitted while loading skills (e.g. skills that loaded but had issues). - [JsonPropertyName("warnings")] - public IList Warnings { get => field ??= []; set; } + /// Exact lifetime AI-credit consumption in non-negative integer nano-AIU, encoded as a decimal string. + [RegularExpression("^[0-9]+$")] + [JsonPropertyName("creditCountNanoAiu")] + public string CreditCountNanoAiu { get; set; } = string.Empty; + + /// Current per-window consumption and optional cap, when a credit-tracking window is present. + [JsonPropertyName("creditLimit")] + public AutopilotObjectiveCreditLimit? CreditLimit { get; set; } + + /// Session-local objective identifier. + [JsonPropertyName("id")] + public long Id { get; set; } + + /// User-provided objective text. + [JsonPropertyName("objective")] + public string Objective { get; set; } = string.Empty; + + /// Optional reason the objective is paused. + [JsonPropertyName("pauseReason")] + public string? PauseReason { get; set; } + + /// Current normalized lifecycle status. + [JsonPropertyName("status")] + public AutopilotObjectiveStatus Status { get; set; } + + /// Number of objective turns started. + [JsonPropertyName("turnCount")] + public long TurnCount { get; set; } } -/// Identifies the target session. +/// Canonical runtime state for the session's current autopilot objective. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSkillsReloadRequest +public sealed class AutopilotObjectiveGetStateResult { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Current objective state, or `null` when the session has no objective. + [JsonPropertyName("state")] + public AutopilotObjectiveState? State { get; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSkillsEnsureLoadedRequest +internal sealed class SessionAutopilotObjectiveGetStateRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// The IDE a host is connected to, as reported to the session. +/// Characters that, when typed in the composer, should trigger a `completions.request`. Empty when the session has no host-driven completions (e.g. local sessions, or a relay host that does not advertise `completionTriggerCharacters`). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionConnectedIdeInfo +public sealed class CompletionsGetTriggerCharactersResult { - /// Display name of the connected IDE, for example `VS Code`. - [JsonPropertyName("ideName")] - public string IdeName { get; set; } = string.Empty; - - /// Absolute path of the workspace folder the IDE has open. - [JsonPropertyName("workspaceFolder")] - public string WorkspaceFolder { get; set; } = string.Empty; + /// Trigger characters advertised by the host (e.g. `["@", "#"]`). Empty disables host-driven completions for the session. + [JsonPropertyName("triggerCharacters")] + public IList TriggerCharacters { get => field ??= []; set; } } -/// Records which IDE the host is connected to, or clears it. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMcpSetConnectedIdeInfoParams +internal sealed class SessionCompletionsGetTriggerCharactersRequest { - /// The connected IDE. Null or omitted clears the recorded IDE, which is how a host reports that it is disconnected. - [JsonPropertyName("ide")] - public SessionConnectedIdeInfo? Ide { get; set; } - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Recorded MCP server connection failure. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpServerFailureInfo -{ - /// Failure message produced when the MCP server connection failed. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; - - /// epoch-ms timestamp at which the failure was recorded. - [JsonPropertyName("timestamp")] - public long Timestamp { get; set; } -} - -/// Recorded MCP server pending-auth state. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpServerNeedsAuthInfo -{ - /// epoch-ms timestamp at which the server signalled it needs authentication. - [JsonPropertyName("timestamp")] - public long Timestamp { get; set; } -} - -/// Host-level state, omitted when no MCP host is initialized. +/// A single host-driven completion. Accepting an item replaces `[rangeStart, rangeEnd)` (UTF-16 code units) in the composer with `insertText`; when the range is absent, the active token around the cursor is replaced. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpHostState +public sealed class SessionCompletionItem { - /// Names of currently-connected MCP clients. - [JsonPropertyName("clients")] - public IList Clients { get => field ??= []; set; } - - /// Configured servers that are explicitly disabled. - [JsonPropertyName("disabledServers")] - public IList DisabledServers { get => field ??= []; set; } - - /// Map of server name to recorded connection failure. - [JsonPropertyName("failedServers")] - public IDictionary FailedServers { get => field ??= new Dictionary(); set; } + /// Text spliced into the composer when the item is accepted. + [JsonPropertyName("insertText")] + public string InsertText { get; set; } = string.Empty; - /// Configured servers filtered out by MCP server policy. - [JsonPropertyName("filteredServers")] - public IList FilteredServers { get => field ??= []; set; } + /// Render-kind hint for the picker row (e.g. `"document"`, `"directory"`), derived from the host's display kind. + [JsonPropertyName("kind")] + public string? Kind { get; set; } - /// Whether third-party MCP servers are policy-enabled for this session. - [JsonPropertyName("mcp3pEnabled")] - public bool Mcp3pEnabled { get; set; } + /// Primary display label for the picker row. Falls back to `insertText` when absent. + [JsonPropertyName("label")] + public string? Label { get; set; } - /// Map of server name to recorded pending-auth state. - [JsonPropertyName("needsAuthServers")] - public IDictionary NeedsAuthServers { get => field ??= new Dictionary(); set; } + /// End (exclusive) of the replacement range in `text`, in UTF-16 code units. + [JsonPropertyName("rangeEnd")] + public long? RangeEnd { get; set; } - /// Names of servers with in-flight connection attempts. - [JsonPropertyName("pendingConnections")] - public IList PendingConnections { get => field ??= []; set; } + /// Start of the replacement range in `text`, in UTF-16 code units. + [JsonPropertyName("rangeStart")] + public long? RangeStart { get; set; } } -/// Owned installation that a listed MCP server's live configuration came from. +/// Host-driven completion items for the current composer input. Empty when the host returns no items or does not support completions. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpServerOwnership +public sealed class CompletionsRequestResult { - /// Stable installation identifier from the owned installation receipt. - [JsonPropertyName("installationId")] - public string InstallationId { get; set; } = string.Empty; + /// Completion items in host-ranked order. + [JsonPropertyName("items")] + public IList Items { get => field ??= []; set; } } -/// MCP server status entry, including config source/plugin source and any connection error. +/// Request host-driven completions for the current composer input. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpServer +internal sealed class CompletionsRequestRequest { - /// Human-readable display name supplied by a managed server catalog. - [JsonPropertyName("displayName")] - public string? DisplayName { get; set; } - - /// Error message if the server failed to connect. - [JsonPropertyName("error")] - public string? Error { get; set; } - - /// Server name (config key). - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - /// Owned installation this entry's live configuration came from. Absent for manual, workspace, plugin, builtin and same-name servers, and on runtimes without owned installations. - [JsonPropertyName("owned")] - public McpServerOwnership? Owned { get; set; } - - /// Server-advertised metadata for a connected server. Omitted when no live connection metadata is available, including while pending or when failed, disabled, stopped, or not configured. - [JsonPropertyName("serverMetadata")] - public McpServerMetadata? ServerMetadata { get; set; } - - /// Configuration source: user, workspace, plugin, builtin, or managed. - [JsonPropertyName("source")] - public McpServerSource? Source { get; set; } - - /// Plugin name that provided this server, when source is plugin. - [JsonPropertyName("sourcePlugin")] - public string? SourcePlugin { get; set; } - - /// Plugin version that provided this server, when source is plugin. - [JsonPropertyName("sourcePluginVersion")] - public string? SourcePluginVersion { get; set; } + /// Cursor offset within `text`, in UTF-16 code units. + [JsonPropertyName("offset")] + public long Offset { get; set; } - /// Connection status: connected, failed, needs-auth, pending, disabled, stopped, or not_configured. - [JsonPropertyName("status")] - public McpServerStatus Status { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Configured URL for an HTTP/SSE server, regardless of configuration source. Omitted for local and in-memory servers. - [JsonPropertyName("url")] - public string? Url { get; set; } + /// The full composed composer input. + [JsonPropertyName("text")] + public string Text { get; set; } = string.Empty; } -/// MCP servers configured for the session, with their connection status and host-level state. +/// Instruction sources loaded for the session, in merge order. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpServerList +public sealed class InstructionsGetSourcesResult { - /// Host-level state, omitted when no MCP host is initialized. - [JsonPropertyName("host")] - public McpHostState? Host { get; set; } - - /// Configured MCP servers. - [JsonPropertyName("servers")] - public IList Servers { get => field ??= []; set; } + /// Instruction sources for the session. + [JsonPropertyName("sources")] + public IList Sources { get => field ??= []; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMcpListRequest +internal sealed class SessionInstructionsGetSourcesRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Observational state for a matching already materialized MCP server. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpConfiguredServerState +internal sealed class SessionInstructionsReloadRequest { - /// Observed connection error, when the materialized server failed. - [JsonPropertyName("error")] - public string? Error { get; set; } - - /// Observed connection status. This is not a configuration or readiness guarantee. - [JsonPropertyName("status")] - public McpServerStatus Status { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Effective MCP configuration entry. Configuration enablement is distinct from the optional live observation. +/// Result of one customization reload component. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpConfiguredServer +public sealed class CustomizationReloadOutcome { - /// Human-readable display name supplied by configuration. - [JsonPropertyName("displayName")] - public string? DisplayName { get; set; } - - /// Whether this configured server is enabled after session configuration and policy filtering. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } - - /// Observed state from an already materialized matching server. Omitted when no live graph has this configured server; it never determines configuration enablement. - [JsonPropertyName("live")] - public McpConfiguredServerState Live { get => field ??= new(); set; } - - /// Server name (config key). - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - /// Configuration provenance: user, workspace, plugin, builtin, or managed. - [JsonPropertyName("source")] - public McpServerSource? Source { get; set; } + /// Reason for a skipped component or description of a failure, when available. + [JsonPropertyName("detail")] + public string? Detail { get; set; } - /// Plugin name that provided this server, when source is plugin. - [JsonPropertyName("sourcePlugin")] - public string? SourcePlugin { get; set; } + /// Whether the component reloaded, was skipped, or failed. + [JsonPropertyName("status")] + public CustomizationReloadStatus Status { get; set; } - /// Plugin version that provided this server, when source is plugin. - [JsonPropertyName("sourcePluginVersion")] - public string? SourcePluginVersion { get; set; } + /// Component whose reload was attempted or skipped. + [JsonPropertyName("subsystem")] + public CustomizationReloadSubsystem Subsystem { get; set; } } -/// Effective MCP configuration with optional live observations from matching already materialized servers. +/// Results of reloading discovered session customizations. Inspect outcomes for reloaded, skipped, or failed subsystems; a rejection may follow partial mutation. Changes to the model-facing prompt and tools apply on the next turn. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpConfiguredServerList +public sealed class CustomizationsReloadResult { - /// Effective configured MCP servers. - [JsonPropertyName("servers")] - public IList Servers { get => field ??= []; set; } + /// Errors from any component that could not be refreshed. + [JsonPropertyName("errors")] + public IList Errors { get => field ??= []; set; } + + /// Outcome of each component in reload order; a skipped component was not configured or loaded. + [JsonPropertyName("outcomes")] + public IList Outcomes { get => field ??= []; set; } + + /// Warnings from skill discovery. + [JsonPropertyName("warnings")] + public IList Warnings { get => field ??= []; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMcpListConfiguredRequest +internal sealed class SessionCustomizationsReloadRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Normalized MCP Apps discovery metadata from a tool's `_meta.ui` block. +/// Indicates whether fleet mode was successfully activated. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpToolUi +public sealed class FleetStartResult { - /// URI of the tool's MCP App resource, typically a `ui://` resource identifier. Use `session.mcp.resources.read` to fetch its HTML and resource metadata. - [JsonPropertyName("resourceUri")] - public string? ResourceUri { get; set; } - - /// Tool visibility advertised by the server. When absent, MCP Apps defaults apply. - [JsonPropertyName("visibility")] - public IList? Visibility { get; set; } + /// Whether fleet mode was successfully activated. + [JsonPropertyName("started")] + public bool Started { get; set; } } -/// MCP tool metadata with tool name, optional description, and normalized MCP Apps discovery metadata. +/// Parameters for starting fleet orchestration: an optional user prompt combined with the fleet instructions, plus the send options forwarded to the resulting turn. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpTools +internal sealed class FleetStartRequest { - /// Tool description, when provided. - [JsonPropertyName("description")] - public string? Description { get; set; } - - /// Tool name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - /// Normalized MCP Apps discovery metadata. An empty object indicates that a valid `_meta.ui` block was present without recognized fields. - [JsonPropertyName("ui")] - public McpToolUi? Ui { get; set; } -} + /// Optional attachments (files, directories, selections, blobs, GitHub references) to include with the fleet request. + [JsonPropertyName("attachments")] + public IList? Attachments { get; set; } -/// Tools exposed by the connected MCP server. Throws when the server is not connected. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpListToolsResult -{ - /// Tools exposed by the server. - [JsonPropertyName("tools")] - public IList Tools { get => field ??= []; set; } -} + /// If false, this request will not trigger a Premium Request Unit charge. User requests default to billable. + [JsonInclude] + [JsonPropertyName("billable")] + internal bool? Billable { get; set; } -/// Server name whose tool list should be returned. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpListToolsRequest -{ - /// Name of the connected MCP server whose tools to list. - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// Optional user prompt to combine with fleet instructions. + [JsonPropertyName("prompt")] + public string? Prompt { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; -} - -/// Name of the MCP server to enable for the session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpEnableRequest -{ - /// Exact receipt identity for explicit owned activation in this session. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } - /// Name of the MCP server to enable. - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } + /// If true, await completion of the agentic loop for this fleet request before returning. Defaults to false. + [JsonPropertyName("wait")] + public bool? Wait { get; set; } } -/// Name of the MCP server to enable for the session. +/// Agents available to the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpEnableRequestWithSession +public sealed class AgentList { - /// Exact receipt identity for explicit owned activation in this session. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } - - /// Name of the MCP server to enable. - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Available agents. + [JsonPropertyName("agents")] + public IList Agents { get => field ??= []; set; } } -/// Name of the MCP server to disable for the session. +/// RPC data type for SessionAgentList operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpDisableRequest +public sealed class SessionAgentListRequest { - /// Required for an owned installation; omission preserves only manual-server behaviour. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } + /// When true, request the session's configured built-in agents alongside custom agents. Listing applies feature, context, inclusion, exclusion, and user-disabled-agent policy, but does not evaluate transient invocation requirements such as model availability. Built-in metadata may be omitted when the session cannot project it, such as a relay session. + [JsonPropertyName("includeBuiltInAgents")] + public bool? IncludeBuiltInAgents { get; set; } - /// Name of the MCP server to disable. - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } + /// When true, request authored base prompt text on each AgentInfo. Prompt text may be omitted when unavailable, such as for agents projected through a relay session. + [JsonPropertyName("includePrompt")] + public bool? IncludePrompt { get; set; } } -/// Name of the MCP server to disable for the session. +/// RPC data type for SessionAgentListRequestWithSession operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpDisableRequestWithSession +internal sealed class SessionAgentListRequestWithSession { - /// Required for an owned installation; omission preserves only manual-server behaviour. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } + /// When true, request the session's configured built-in agents alongside custom agents. Listing applies feature, context, inclusion, exclusion, and user-disabled-agent policy, but does not evaluate transient invocation requirements such as model availability. Built-in metadata may be omitted when the session cannot project it, such as a relay session. + [JsonPropertyName("includeBuiltInAgents")] + public bool? IncludeBuiltInAgents { get; set; } - /// Name of the MCP server to disable. - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// When true, request authored base prompt text on each AgentInfo. Prompt text may be omitted when unavailable, such as for agents projected through a relay session. + [JsonPropertyName("includePrompt")] + public bool? IncludePrompt { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Identifies the target session. +/// An in-memory authored prompt override for an available agent. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMcpReloadRequest +internal sealed class AgentSetPromptRequest { + /// Stable effective agent id. Plugin namespace separators are normalized. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Replacement authored prompt. Empty text is valid. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of moving in-flight MCP loading to the background. +/// The currently selected custom agent, or null when using the default agent. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MoveMcpLoadingToBackgroundResult +public sealed class AgentGetCurrentResult { - /// Whether an in-flight MCP load was moved to the background, releasing turns that were waiting on it. False when no MCP load was in flight or the waiting turns had already been released. - [JsonPropertyName("movedToBackground")] - public bool MovedToBackground { get; set; } + /// Currently selected custom agent, or null if using the default agent. + [JsonPropertyName("agent")] + public AgentInfo? Agent { get; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMcpMoveLoadingToBackgroundRequest +internal sealed class SessionAgentGetCurrentRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// MCP server allowed by policy, with server name and optional PII-free explanatory note. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAllowedServer -{ - /// Allowed server name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - /// PII-free note explaining why the server was allowed. - [JsonPropertyName("redactedNote")] - public string? RedactedNote { get; set; } -} - -/// MCP server whose connection attempt failed. +/// The newly selected custom agent. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpFailedServer +public sealed class AgentSelectResult { - /// The captured connection failure detail. - [JsonPropertyName("error")] - public string? Error { get; set; } - - /// The config key of the server that failed to connect. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// The newly selected custom agent. + [JsonPropertyName("agent")] + public AgentInfo Agent { get => field ??= new(); set; } } -/// MCP server filtered by policy, with name, reason, and optional redacted reason. +/// Name of the custom agent to select for subsequent turns. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpFilteredServer +internal sealed class AgentSelectRequest { - /// Deprecated. This field is no longer populated. - [EditorBrowsable(EditorBrowsableState.Never)] -#if NET5_0_OR_GREATER - [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] -#else - [Obsolete("This member is deprecated and will be removed in a future version.")] -#endif - [JsonPropertyName("enterpriseName")] - public string? EnterpriseName { get; set; } - - /// Filtered server name. + /// Name of the custom agent to select. [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; - /// Human-readable filter reason. - [JsonPropertyName("reason")] - public string Reason { get; set; } = string.Empty; - - /// PII-free filter reason. - [JsonPropertyName("redactedReason")] - public string? RedactedReason { get; set; } -} - -/// MCP server startup filtering result. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpStartServersResult -{ - /// Non-default servers allowed by policy. - [JsonPropertyName("allowedServers")] - public IList? AllowedServers { get; set; } - - /// Servers whose connection attempt failed. - [JsonPropertyName("failedServers")] - public IList? FailedServers { get; set; } - - /// Servers filtered out before startup. - [JsonPropertyName("filteredServers")] - public IList FilteredServers { get => field ??= []; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Opaque MCP reload configuration. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpReloadWithConfigRequest +internal sealed class SessionAgentDeselectRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// MCP CreateMessageResult payload (with optional 'tools' extension), present when action='success'. Treated as opaque at the schema layer; consumers should construct/consume it per the MCP CreateMessageResult shape. +/// Custom agents available to the session after reloading definitions from disk. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpExecuteSamplingResult +public sealed class AgentReloadResult { + /// Reloaded custom agents. + [JsonPropertyName("agents")] + public IList Agents { get => field ??= []; set; } } -/// Outcome of an MCP sampling execution: success result, failure error, or cancellation. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpSamplingExecutionResult +internal sealed class SessionAgentReloadRequest { - /// Outcome of the sampling inference. 'success' produced a response; 'failure' encountered an error (including agent-side rejection by content filter or criteria); 'cancelled' the caller cancelled this execution via cancelSamplingExecution. - [JsonPropertyName("action")] - public McpSamplingExecutionAction Action { get; set; } - - /// Error description, present when action='failure'. - [JsonPropertyName("error")] - public string? Error { get; set; } - - /// MCP CreateMessageResult payload (with optional 'tools' extension), present when action='success'. Treated as opaque at the schema layer; consumers should construct/consume it per the MCP CreateMessageResult shape. - [JsonPropertyName("result")] - public McpExecuteSamplingResult? Result { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Raw MCP CreateMessageRequest params, as received in the `sampling.requested` event. Treated as opaque at the schema layer; the runtime converts the embedded MCP messages into the OpenAI chat-completion shape internally. +/// Identifier assigned to the newly started background agent task. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpExecuteSamplingRequest +public sealed class TasksStartAgentResult { + /// Generated agent ID for the background task. + [JsonPropertyName("agentId")] + public string AgentId { get; set; } = string.Empty; } -/// Identifiers and raw MCP CreateMessageRequest params used to run a sampling inference. +/// Agent type, prompt, name, and optional description and model override for the new task. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpExecuteSamplingParams +internal sealed class TasksStartAgentRequest { - /// The original MCP JSON-RPC request ID (string or number). Used by the runtime to correlate the inference with the originating MCP request for telemetry; this is distinct from `requestId` (which is the schema-level cancellation handle). - [JsonPropertyName("mcpRequestId")] - public JsonElement McpRequestId { get; set; } - - /// Raw MCP CreateMessageRequest params, as received in the `sampling.requested` event. Treated as opaque at the schema layer; the runtime converts the embedded MCP messages into the OpenAI chat-completion shape internally. - [JsonPropertyName("request")] - public McpExecuteSamplingRequest Request { get => field ??= new(); set; } - - /// Caller-provided unique identifier for this sampling execution. Use this same ID with cancelSamplingExecution to cancel the in-flight call. Must be unique within the session for the lifetime of the call. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// Type of agent to start (e.g., 'explore', 'task', 'general-purpose'). + [JsonPropertyName("agentType")] + public string AgentType { get; set; } = string.Empty; - /// Name of the MCP server that initiated the sampling request. - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// Short description of the task. + [JsonPropertyName("description")] + public string? Description { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Optional model override. + [JsonPropertyName("model")] + public string? Model { get; set; } -/// Indicates whether an in-flight sampling execution with the given requestId was found and cancelled. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpCancelSamplingExecutionResult -{ - /// True if an in-flight execution with the given requestId was found and signalled to cancel. False when no such execution is in flight (already completed, never started, or cancelled by another caller). - [JsonPropertyName("cancelled")] - public bool Cancelled { get; set; } -} + /// Friendly, non-unique name used when displaying the agent. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; -/// The requestId previously passed to executeSampling that should be cancelled. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpCancelSamplingExecutionParams -{ - /// The requestId previously passed to executeSampling that should be cancelled. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// Task prompt for the agent. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Env-value mode recorded on the session after the update. +/// Tracked task union returned by task APIs, containing an agent, client, or shell task. +/// Polymorphic base type discriminated by type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpSetEnvValueModeResult +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "type", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(TaskInfoAgent), "agent")] +[JsonDerivedType(typeof(TaskInfoClient), "client")] +[JsonDerivedType(typeof(TaskInfoShell), "shell")] +public partial class TaskInfo { - /// Mode recorded on the session after the update. - [JsonPropertyName("mode")] - public McpSetEnvValueModeDetails Mode { get; set; } + /// The type discriminator. + [JsonPropertyName("type")] + public virtual string Type { get; set; } = string.Empty; } -/// Mode controlling how MCP server env values are resolved (`direct` or `indirect`). + +/// Tracked background agent task metadata, including IDs, status, timing, agent type, prompt, model, result, and latest response. +/// The agent variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpSetEnvValueModeParams +public partial class TaskInfoAgent : TaskInfo { - /// How environment-variable values supplied to MCP servers are resolved. "direct" passes literal string values; "indirect" treats values as references (e.g. names of environment variables on the host) that the runtime resolves before launch. Defaults to the runtime's startup mode; clients that intentionally launch MCP servers with literal values (e.g. CLI prompt mode and ACP) set this to "direct". - [JsonPropertyName("mode")] - public McpSetEnvValueModeDetails Mode { get; set; } + /// + [JsonIgnore] + public override string Type => "agent"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// ISO 8601 timestamp when the current active period began. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("activeStartedAt")] + public DateTimeOffset? ActiveStartedAt { get; set; } -/// Indicates whether the auto-managed `github` MCP server was removed (false when nothing to remove). -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpRemoveGitHubResult -{ - /// True when the auto-managed `github` MCP server was removed; false when no removal happened (e.g. user has explicitly configured a `github` server, or the server was not registered). - [JsonPropertyName("removed")] - public bool Removed { get; set; } -} + /// Accumulated active execution time in milliseconds. + [JsonConverter(typeof(MillisecondsTimeSpanConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("activeTimeMs")] + public TimeSpan? ActiveTime { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMcpRemoveGitHubRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Type of agent running this task. + [JsonPropertyName("agentType")] + public required string AgentType { get; set; } -/// Result of configuring GitHub MCP. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpConfigureGitHubResult -{ - /// Whether GitHub MCP configuration changed. - [JsonPropertyName("changed")] - public bool Changed { get; set; } -} + /// Whether the task is currently in the original sync wait and can be moved to background mode. False once it is already backgrounded, idle, finished, or no longer has a promotable sync waiter. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("canPromoteToBackground")] + public bool? CanPromoteToBackground { get; set; } -/// Credential-free authentication identity used to configure GitHub MCP. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpConfigureGitHubRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// ISO 8601 timestamp when the task finished. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("completedAt")] + public DateTimeOffset? CompletedAt { get; set; } -/// Server name and optional configuration for an individual MCP server start. Omit `config` for a config-free start-by-name of an already-configured server. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpStartServerRequest -{ - /// MCP server configuration (stdio process or remote HTTP/SSE). Omit to start the server with its already-registered configuration (config-free start-by-name). - [JsonPropertyName("config")] - public JsonElement? Config { get; set; } + /// Short description of the task. + [JsonPropertyName("description")] + public required string Description { get; set; } - /// Exact receipt identity for explicit owned activation in this session. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } + /// Friendly, non-unique name intended for display. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("displayName")] + public string? DisplayName { get; set; } - /// Name of the MCP server to start. - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } -} + /// Error message when the task failed. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("error")] + public string? Error { get; set; } -/// Server name and optional configuration for an individual MCP server start. Omit `config` for a config-free start-by-name of an already-configured server. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpStartServerRequestWithSession -{ - /// MCP server configuration (stdio process or remote HTTP/SSE). Omit to start the server with its already-registered configuration (config-free start-by-name). - [JsonPropertyName("config")] - public JsonElement? Config { get; set; } + /// Whether task execution is synchronously awaited or managed in the background. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("executionMode")] + public TaskExecutionMode? ExecutionMode { get; set; } - /// Exact receipt identity for explicit owned activation in this session. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } + /// Unique task identifier. + [JsonPropertyName("id")] + public required string Id { get; set; } - /// Name of the MCP server to start. - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// ISO 8601 timestamp when the agent entered idle state. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("idleSince")] + public DateTimeOffset? IdleSince { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Most recent response text from the agent. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("latestResponse")] + public string? LatestResponse { get; set; } -/// Server name and optional replacement configuration for an individual MCP server restart. Omit `config` for a config-free restart-by-name of an already-configured server. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpRestartServerRequest -{ - /// Replacement MCP server configuration (stdio process or remote HTTP/SSE). Omit to restart the server with its already-registered configuration (config-free restart-by-name). - [JsonPropertyName("config")] - public JsonElement? Config { get; set; } + /// Requested model override for the task when specified. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("model")] + public string? Model { get; set; } - /// Exact receipt identity for an explicit owned restart; configuration overrides are refused. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } + /// Most recent prompt delivered to the agent. Updated whenever the agent receives a follow-up message. + [JsonPropertyName("prompt")] + public required string Prompt { get; set; } - /// Name of the MCP server to restart. - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } -} + /// Runtime model resolved for the task when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("resolvedModel")] + public string? ResolvedModel { get; set; } -/// Server name and optional replacement configuration for an individual MCP server restart. Omit `config` for a config-free restart-by-name of an already-configured server. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpRestartServerRequestWithSession -{ - /// Replacement MCP server configuration (stdio process or remote HTTP/SSE). Omit to restart the server with its already-registered configuration (config-free restart-by-name). - [JsonPropertyName("config")] - public JsonElement? Config { get; set; } + /// Result text from the task when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("result")] + public string? Result { get; set; } - /// Exact receipt identity for an explicit owned restart; configuration overrides are refused. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } + /// ISO 8601 timestamp when the task was started. + [JsonPropertyName("startedAt")] + public required DateTimeOffset StartedAt { get; set; } - /// Name of the MCP server to restart. - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// Current lifecycle status of the task. + [JsonPropertyName("status")] + public required TaskStatus Status { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Tool call ID associated with this agent task. + [JsonPropertyName("toolCallId")] + public required string ToolCallId { get; set; } } -/// Server name for an individual MCP server stop. +/// Public owner attribution for a client-owned task. Identifiers are opaque and never authorize requests. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpStopServerRequest +public sealed class TaskClientOwner { - /// Exact owned receipt identity. Stop also forgets this session's durable activation. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } + /// ISO 8601 timestamp when the bound join disconnected. + [JsonPropertyName("disconnectedAt")] + public DateTimeOffset? DisconnectedAt { get; set; } - /// Name of the MCP server to stop. - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } -} + /// Display-only owner name. + [JsonPropertyName("displayName")] + public string? DisplayName { get; set; } -/// Server name for an individual MCP server stop. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpStopServerRequestWithSession + /// Opaque identity of the currently or most recently bound session join. + [JsonPropertyName("joinId")] + public string JoinId { get; set; } = string.Empty; + + /// Class of the task owner. + [JsonPropertyName("kind")] + public TaskClientOwnerKind Kind { get; set; } + + /// Opaque session-scoped participant identity. + [JsonPropertyName("participantId")] + public string ParticipantId { get; set; } = string.Empty; + + /// Whether this task's bound join is currently connected. + [JsonPropertyName("presence")] + public TaskClientOwnerPresence Presence { get; set; } + + /// Display-only owner source. + [JsonPropertyName("source")] + public string? Source { get; set; } +} + +/// Tracked client-owned task metadata. +/// The client variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class TaskInfoClient : TaskInfo { - /// Exact owned receipt identity. Stop also forgets this session's durable activation. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } + /// + [JsonIgnore] + public override string Type => "client"; - /// Name of the MCP server to stop. - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// ISO 8601 timestamp when the current active segment started. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("activeStartedAt")] + public DateTimeOffset? ActiveStartedAt { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Accumulated active execution time in milliseconds. + [JsonPropertyName("activeTimeMs")] + public required long ActiveTimeMs { get; set; } + + /// Whether the currently bound owner can receive a cancellation request. + [JsonPropertyName("canCancel")] + public required bool CanCancel { get; set; } + + /// Human-readable reason for terminal cancellation. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("cancellationReason")] + public string? CancellationReason { get; set; } + + /// Owner-scoped registration and reclaim key. + [JsonPropertyName("clientTaskId")] + public required string ClientTaskId { get; set; } + + /// ISO 8601 timestamp when the task reached a terminal status. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("completedAt")] + public DateTimeOffset? CompletedAt { get; set; } + + /// Task description. + [JsonPropertyName("description")] + public required string Description { get; set; } + + /// Optional task display name. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("displayName")] + public string? DisplayName { get; set; } + + /// Human-readable terminal failure message. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("error")] + public string? Error { get; set; } + + /// Optional owner-supplied terminal failure code. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("errorCode")] + public string? ErrorCode { get; set; } + + /// Execution mode, which is always background for client-owned tasks. + [JsonPropertyName("executionMode")] + public required TaskClientExecutionMode ExecutionMode { get; set; } + + /// Canonical runtime-generated task identifier. + [JsonPropertyName("id")] + public required string Id { get; set; } + + /// ISO 8601 timestamp when the connected owner entered idle status. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("idleSince")] + public DateTimeOffset? IdleSince { get; set; } + + /// ISO 8601 timestamp of the most recent orphan transition. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("orphanedAt")] + public DateTimeOffset? OrphanedAt { get; set; } + + /// Public attribution and presence for the task owner. + [JsonPropertyName("owner")] + public required TaskClientOwner Owner { get; set; } + + /// ISO 8601 timestamp of the most recent successful reclaim. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("reclaimedAt")] + public DateTimeOffset? ReclaimedAt { get; set; } + + /// Opaque successful terminal result supplied by the task owner. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("result")] + public JsonElement? Result { get; set; } + + /// Sequence number of the latest accepted owner update. + [JsonPropertyName("sequence")] + public required long Sequence { get; set; } + + /// ISO 8601 timestamp when the task started. + [JsonPropertyName("startedAt")] + public required DateTimeOffset StartedAt { get; set; } + + /// Client task lifecycle status. + [JsonPropertyName("status")] + public required TaskClientStatus Status { get; set; } + + /// ISO 8601 timestamp of the latest accepted lifecycle change. + [JsonPropertyName("updatedAt")] + public required DateTimeOffset UpdatedAt { get; set; } } -/// Registration parameters for an external MCP client. +/// Tracked shell task metadata, including ID, command, status, timing, attachment/execution mode, log path, and PID. +/// The shell variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpRegisterExternalClientRequest +public partial class TaskInfoShell : TaskInfo { - /// Logical server name for the external client. - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Type => "shell"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether the shell runs inside a managed PTY session or as an independent background process. + [JsonPropertyName("attachmentMode")] + public required TaskShellInfoAttachmentMode AttachmentMode { get; set; } + + /// Whether this shell task can be promoted to background mode. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("canPromoteToBackground")] + public bool? CanPromoteToBackground { get; set; } + + /// Command being executed. + [JsonPropertyName("command")] + public required string Command { get; set; } + + /// ISO 8601 timestamp when the task finished. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("completedAt")] + public DateTimeOffset? CompletedAt { get; set; } + + /// Short description of the task. + [JsonPropertyName("description")] + public required string Description { get; set; } + + /// Whether task execution is synchronously awaited or managed in the background. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("executionMode")] + public TaskExecutionMode? ExecutionMode { get; set; } + + /// Unique task identifier. + [JsonPropertyName("id")] + public required string Id { get; set; } + + /// Path to the detached shell log, when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("logPath")] + public string? LogPath { get; set; } + + /// Process ID when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("pid")] + public long? Pid { get; set; } + + /// ISO 8601 timestamp when the task was started. + [JsonPropertyName("startedAt")] + public required DateTimeOffset StartedAt { get; set; } + + /// Current lifecycle status of the task. + [JsonPropertyName("status")] + public required TaskStatus Status { get; set; } } -/// Server name identifying the external client to remove. +/// Background tasks currently tracked by the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpUnregisterExternalClientRequest +public sealed class TaskList { - /// Server name of the external client to unregister. - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// Currently tracked tasks. + [JsonPropertyName("tasks")] + public IList Tasks { get => field ??= []; set; } +} +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionTasksListRequest +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Whether the named MCP server is running. +/// Tracked client-owned task metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpIsServerRunningResult +public sealed class TaskClientInfo { - /// True if the server has an active client and transport. - [JsonPropertyName("running")] - public bool Running { get; set; } + /// ISO 8601 timestamp when the current active segment started. + [JsonPropertyName("activeStartedAt")] + public DateTimeOffset? ActiveStartedAt { get; set; } + + /// Accumulated active execution time in milliseconds. + [JsonPropertyName("activeTimeMs")] + public long ActiveTimeMs { get; set; } + + /// Whether the currently bound owner can receive a cancellation request. + [JsonPropertyName("canCancel")] + public bool CanCancel { get; set; } + + /// Human-readable reason for terminal cancellation. + [JsonPropertyName("cancellationReason")] + public string? CancellationReason { get; set; } + + /// Owner-scoped registration and reclaim key. + [JsonPropertyName("clientTaskId")] + public string ClientTaskId { get; set; } = string.Empty; + + /// ISO 8601 timestamp when the task reached a terminal status. + [JsonPropertyName("completedAt")] + public DateTimeOffset? CompletedAt { get; set; } + + /// Task description. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; + + /// Optional task display name. + [JsonPropertyName("displayName")] + public string? DisplayName { get; set; } + + /// Human-readable terminal failure message. + [JsonPropertyName("error")] + public string? Error { get; set; } + + /// Optional owner-supplied terminal failure code. + [JsonPropertyName("errorCode")] + public string? ErrorCode { get; set; } + + /// Execution mode, which is always background for client-owned tasks. + [JsonPropertyName("executionMode")] + public TaskClientExecutionMode ExecutionMode { get; set; } + + /// Canonical runtime-generated task identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// ISO 8601 timestamp when the connected owner entered idle status. + [JsonPropertyName("idleSince")] + public DateTimeOffset? IdleSince { get; set; } + + /// ISO 8601 timestamp of the most recent orphan transition. + [JsonPropertyName("orphanedAt")] + public DateTimeOffset? OrphanedAt { get; set; } + + /// Public attribution and presence for the task owner. + [JsonPropertyName("owner")] + public TaskClientOwner Owner { get => field ??= new(); set; } + + /// ISO 8601 timestamp of the most recent successful reclaim. + [JsonPropertyName("reclaimedAt")] + public DateTimeOffset? ReclaimedAt { get; set; } + + /// Opaque successful terminal result supplied by the task owner. + [JsonPropertyName("result")] + public JsonElement? Result { get; set; } + + /// Sequence number of the latest accepted owner update. + [JsonPropertyName("sequence")] + public long Sequence { get; set; } + + /// ISO 8601 timestamp when the task started. + [JsonPropertyName("startedAt")] + public DateTimeOffset StartedAt { get; set; } + + /// Client task lifecycle status. + [JsonPropertyName("status")] + public TaskClientStatus Status { get; set; } + + /// Task kind. + [JsonPropertyName("type")] + public TaskClientType Type { get; set; } + + /// ISO 8601 timestamp of the latest accepted lifecycle change. + [JsonPropertyName("updatedAt")] + public DateTimeOffset UpdatedAt { get; set; } } -/// Server name to check running status for. +/// Result of registering or reclaiming a client-owned task. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpIsServerRunningRequest +public sealed class TasksRegisterResult { - /// Name of the MCP server to check. - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// True only when this invocation created a new task. + [JsonPropertyName("created")] + public bool Created { get; set; } + + /// True only when this invocation reclaimed an orphaned task. + [JsonPropertyName("reclaimed")] + public bool Reclaimed { get; set; } + + /// Authoritative registered or reclaimed task. + [JsonPropertyName("task")] + public TaskClientInfo Task { get => field ??= new(); set; } +} + +/// Registers or reclaims a client-owned task. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class TasksRegisterRequest +{ + /// Whether the owner supports runtime cancellation requests. + [JsonPropertyName("cancellable")] + public bool Cancellable { get; set; } + + /// Owner-scoped idempotency key used for registration and reclaim. + [JsonPropertyName("clientTaskId")] + public string ClientTaskId { get; set; } = string.Empty; + + /// Human-readable description of the external work. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; + + /// Optional short display name for the external work. + [JsonPropertyName("displayName")] + public string? DisplayName { get; set; } + + /// Expected current sequence for idempotent registration or orphan reclaim. + [JsonPropertyName("expectedSequence")] + public long? ExpectedSequence { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Task kind. + [JsonPropertyName("type")] + public TaskClientType Type { get; set; } } -/// Indicates whether the pending MCP OAuth response was accepted. +/// Result of publishing a client-owned task update. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpOauthHandlePendingResult +public sealed class TasksUpdateResult { - /// Whether the response was accepted. False if the request was unknown, timed out, or already resolved. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Whether this invocation changed task state. + [JsonPropertyName("applied")] + public bool Applied { get; set; } + + /// Whether this invocation repeated the latest accepted update. + [JsonPropertyName("duplicate")] + public bool Duplicate { get; set; } + + /// Authoritative task after processing the update. + [JsonPropertyName("task")] + public TaskClientInfo Task { get => field ??= new(); set; } } -/// Host response to the pending OAuth request. +/// Progress or terminal update for a client-owned task. /// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonPolymorphic( TypeDiscriminatorPropertyName = "kind", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(McpOauthPendingRequestResponseToken), "token")] -[JsonDerivedType(typeof(McpOauthPendingRequestResponseCancelled), "cancelled")] -public partial class McpOauthPendingRequestResponse +[JsonDerivedType(typeof(TaskClientUpdateProgress), "progress")] +[JsonDerivedType(typeof(TaskClientUpdateCompleted), "completed")] +[JsonDerivedType(typeof(TaskClientUpdateFailed), "failed")] +[JsonDerivedType(typeof(TaskClientUpdateCancelled), "cancelled")] +public partial class TaskClientUpdate { /// The type discriminator. [JsonPropertyName("kind")] @@ -17325,1177 +18138,852 @@ public partial class McpOauthPendingRequestResponse } -/// The token variant of . +/// Publishes nonterminal progress for a running or idle client task. +/// The progress variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpOauthPendingRequestResponseToken : McpOauthPendingRequestResponse +public partial class TaskClientUpdateProgress : TaskClientUpdate { /// [JsonIgnore] - public override string Kind => "token"; + public override string Kind => "progress"; - /// Access token acquired by the SDK host. - [JsonPropertyName("accessToken")] - public required string AccessToken { get; set; } + /// Optional progress message appended to recent activity when nonempty. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("message")] + public string? Message { get; set; } - /// Token lifetime in seconds, if known. + /// Optional completion percentage; null clears the current percentage. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("expiresIn")] - public long? ExpiresIn { get; set; } + [JsonPropertyName("percentage")] + public double? Percentage { get; set; } - /// OAuth token type. Defaults to bearer when omitted. + /// Optional progress phase; null clears the current phase. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("tokenType")] - public string? TokenType { get; set; } + [JsonPropertyName("phase")] + public string? Phase { get; set; } + + /// Optional active status transition. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("status")] + public TaskClientActiveStatus? Status { get; set; } } -/// The cancelled variant of . +/// Reports successful terminal completion. +/// The completed variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpOauthPendingRequestResponseCancelled : McpOauthPendingRequestResponse +public partial class TaskClientUpdateCompleted : TaskClientUpdate { /// [JsonIgnore] - public override string Kind => "cancelled"; + public override string Kind => "completed"; + + /// Optional final progress message. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("message")] + public string? Message { get; set; } + + /// Optional opaque successful terminal result. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("result")] + public JsonElement? Result { get; set; } } -/// Pending MCP OAuth request ID and host-provided token or cancellation response. +/// Reports terminal failure. +/// The failed variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpOauthHandlePendingRequest +public partial class TaskClientUpdateFailed : TaskClientUpdate { - /// OAuth request identifier from the mcp.oauth_required event. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "failed"; - /// Host response to the pending OAuth request. - [JsonPropertyName("result")] - public McpOauthPendingRequestResponse Result { get => field ??= new(); set; } + /// Optional owner-supplied terminal failure code. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("code")] + public string? Code { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Human-readable terminal failure message. + [JsonPropertyName("error")] + public required string Error { get; set; } + + /// Optional final progress message. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("message")] + public string? Message { get; set; } } -/// Identifies the MCP server whose persisted OAuth credentials were updated. +/// Reports terminal cancellation after external work stopped. +/// The cancelled variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpOauthAuthenticationStateChangedRequest +public partial class TaskClientUpdateCancelled : TaskClientUpdate { - /// Whether the target session must mint a session-scoped access token instead of reusing a shared access token persisted by another session. - [JsonPropertyName("refreshSessionToken")] - public bool? RefreshSessionToken { get; set; } + /// + [JsonIgnore] + public override string Kind => "cancelled"; - /// Name of the MCP server whose OAuth credentials were updated. Omit only when the host cannot identify the server. - [JsonPropertyName("serverName")] - public string? ServerName { get; set; } + /// Optional final progress message. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("message")] + public string? Message { get; set; } + + /// Optional human-readable cancellation reason. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("reason")] + public string? Reason { get; set; } +} + +/// Updates a client-owned task. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class TasksUpdateRequest +{ + /// Canonical runtime-generated task identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Owner update sequence to apply. + [JsonPropertyName("sequence")] + public long Sequence { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Progress or terminal update payload. + [JsonPropertyName("update")] + public TaskClientUpdate Update { get => field ??= new(); set; } } -/// An inert runtime-issued login handle. Preparation alone performs no activation or OAuth work. +/// 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. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionMcpOauthPrepareLoginResult +public sealed class TasksRefreshResult { - /// Original expiry, not extended by consumption, retries or cancellation. - [JsonPropertyName("expiresAt")] - public DateTimeOffset ExpiresAt { get; set; } - - /// Retain with the original requester and use for one login or cancellation. - [JsonPropertyName("loginId")] - public string LoginId { get; set; } = string.Empty; } -/// Effect-free preparation bound to the existing local session, requester and installation, with frozen options. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMcpOauthPrepareLoginRequest +internal sealed class SessionTasksRefreshRequest { - /// Text shown on the loopback callback page after successful authorisation. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(2048)] - [JsonPropertyName("callbackSuccessMessage")] - public string? CallbackSuccessMessage { get; set; } - - /// Display name used by the incumbent OAuth client-registration flow. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(2048)] - [JsonPropertyName("clientName")] - public string? ClientName { get; set; } - - /// Exact installation identity from owned inventory, never a server-name alias. - [JsonPropertyName("expectedInstallationId")] - public string ExpectedInstallationId { get; set; } = string.Empty; - - /// Request a new authorisation rather than accepting a usable cached grant. - [JsonPropertyName("forceReauth")] - public bool? ForceReauth { get; set; } - - /// Name recorded by the authoritative owned installation receipt. - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// OAuth authorization URL the caller should open, or empty when cached tokens already authenticated the server. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpOauthLoginResult -{ - /// Opaque authorization identifier returned only for a host-managed redirect URI. The runtime also sends it as the OAuth state value, so the callback endpoint can read state and pass it with the full callback URL to session.mcp.oauth.complete. - [JsonPropertyName("authorizationId")] - public string? AuthorizationId { get; set; } - - /// URL the caller should open in a browser to complete OAuth. Omitted when cached tokens were still valid and no browser interaction was needed — the server is already reconnected in that case. For the default loopback flow, the runtime starts its listener before returning. With redirectUri, the host receives the callback and completes it through session.mcp.oauth.complete. The runtime continues the flow in the background and signals completion via session.mcp_server_status_changed. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("authorizationUrl")] - public string? AuthorizationUrl { get; set; } - - /// Runtime-issued owned flow identity; never a server name or installation operation ID. - [JsonPropertyName("loginId")] - public string? LoginId { get; set; } - - /// Explicit outcome for owned sign-in. Manual callers retain their legacy response shape. - [JsonPropertyName("status")] - public McpOwnedOauthLoginStatus? Status { get; set; } -} - -/// Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, callback handling, and static OAuth client selection. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpOauthLoginRequest -{ - /// Optional override for the body text shown on the OAuth loopback callback success page. When omitted, the runtime applies a neutral fallback; callers driving interactive auth should pass surface-specific copy telling the user where to return. - [JsonPropertyName("callbackSuccessMessage")] - public string? CallbackSuccessMessage { get; set; } - - /// Optional OAuth client ID override for this login. When set, the runtime uses this pre-registered static client instead of dynamic client registration. - [JsonPropertyName("clientId")] - public string? ClientId { get; set; } - - /// Optional override for the OAuth client display name shown on the consent screen. Applies to newly registered dynamic clients only — existing registrations keep the name they were created with. When omitted, the runtime applies a neutral fallback; callers driving interactive auth should pass their own surface-specific label so the consent screen matches the product the user sees. - [JsonPropertyName("clientName")] - public string? ClientName { get; set; } - - /// Optional OAuth client secret override for this login. The runtime treats this as an ephemeral host-owned secret, uses it for this authentication attempt and does not persist it. - [JsonPropertyName("clientSecret")] - public string? ClientSecret { get; set; } - - /// Exact owned receipt identity. Owned login never uses an implicit helper session. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } - - /// When true, clears any cached OAuth token for the server and runs a full new authorization. Use when the user explicitly wants to switch accounts or believes their session is stuck. - [JsonPropertyName("forceReauth")] - public bool? ForceReauth { get; set; } - - /// Optional OAuth grant type override for this login. Defaults to the server configuration, or authorization_code when no grant type is specified. - [JsonPropertyName("grantType")] - public McpOauthLoginGrantType? GrantType { get; set; } - - /// - /// Required for owned login. Consumes the exact prepareLogin handle once. - /// Set forceReauth and display options during preparation, not consumption. - /// - [JsonPropertyName("loginId")] - public string? LoginId { get; set; } - - /// Optional override indicating whether the static OAuth client is public. When false, the runtime treats it as confidential and uses the per-login clientSecret if provided, otherwise retrieving the client secret from the MCP OAuth secret store. - [JsonPropertyName("publicClient")] - public bool? PublicClient { get; set; } - - /// Optional externally visible HTTPS redirect URI for a host-managed callback endpoint. When supplied, the runtime still owns discovery, PKCE, token exchange, persistence, and reconnect, but does not bind a loopback listener or terminate HTTPS. The URI must not contain query parameters or a fragment and must be registered for the selected CIMD, DCR, or static OAuth client. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("redirectUri")] - public string? RedirectUri { get; set; } - - /// Name of the remote MCP server to authenticate. - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } -} - -/// Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, callback handling, and static OAuth client selection. +/// 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). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpOauthLoginRequestWithSession +public sealed class TasksWaitForPendingResult { - /// Optional override for the body text shown on the OAuth loopback callback success page. When omitted, the runtime applies a neutral fallback; callers driving interactive auth should pass surface-specific copy telling the user where to return. - [JsonPropertyName("callbackSuccessMessage")] - public string? CallbackSuccessMessage { get; set; } - - /// Optional OAuth client ID override for this login. When set, the runtime uses this pre-registered static client instead of dynamic client registration. - [JsonPropertyName("clientId")] - public string? ClientId { get; set; } - - /// Optional override for the OAuth client display name shown on the consent screen. Applies to newly registered dynamic clients only — existing registrations keep the name they were created with. When omitted, the runtime applies a neutral fallback; callers driving interactive auth should pass their own surface-specific label so the consent screen matches the product the user sees. - [JsonPropertyName("clientName")] - public string? ClientName { get; set; } - - /// Optional OAuth client secret override for this login. The runtime treats this as an ephemeral host-owned secret, uses it for this authentication attempt and does not persist it. - [JsonPropertyName("clientSecret")] - public string? ClientSecret { get; set; } - - /// Exact owned receipt identity. Owned login never uses an implicit helper session. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } - - /// When true, clears any cached OAuth token for the server and runs a full new authorization. Use when the user explicitly wants to switch accounts or believes their session is stuck. - [JsonPropertyName("forceReauth")] - public bool? ForceReauth { get; set; } - - /// Optional OAuth grant type override for this login. Defaults to the server configuration, or authorization_code when no grant type is specified. - [JsonPropertyName("grantType")] - public McpOauthLoginGrantType? GrantType { get; set; } - - /// - /// Required for owned login. Consumes the exact prepareLogin handle once. - /// Set forceReauth and display options during preparation, not consumption. - /// - [JsonPropertyName("loginId")] - public string? LoginId { get; set; } - - /// Optional override indicating whether the static OAuth client is public. When false, the runtime treats it as confidential and uses the per-login clientSecret if provided, otherwise retrieving the client secret from the MCP OAuth secret store. - [JsonPropertyName("publicClient")] - public bool? PublicClient { get; set; } - - /// Optional externally visible HTTPS redirect URI for a host-managed callback endpoint. When supplied, the runtime still owns discovery, PKCE, token exchange, persistence, and reconnect, but does not bind a loopback listener or terminate HTTPS. The URI must not contain query parameters or a fragment and must be registered for the selected CIMD, DCR, or static OAuth client. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("redirectUri")] - public string? RedirectUri { get; set; } - - /// Name of the remote MCP server to authenticate. - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; } -/// Host-delivered callback for a runtime-managed MCP OAuth login. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpOauthCompleteRequest +internal sealed class SessionTasksWaitForPendingRequest { - /// Opaque identifier returned by session.mcp.oauth.login for the pending external callback. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("authorizationId")] - public string AuthorizationId { get; set; } = string.Empty; - - /// Full externally visible HTTPS callback URL received by the host, including the authorization response query parameters. Applications behind a reverse proxy must reconstruct the public URL rather than passing an internal proxy URL. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("callbackUrl")] - public string CallbackUrl { get; set; } = string.Empty; - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Passive MCP OAuth probe result. `authenticated` means the server accepted the probe request while an OAuth-origin access token was attached; it does not prove the server required or independently validated that token. The probe does not make a second unauthenticated request. Failed is an expected probe-domain outcome; JSON-RPC errors are reserved for API-call failures. -/// Polymorphic base type discriminated by status. +/// Progress information for the task, discriminated by type. Returns null when no task with this ID is currently tracked. +/// Polymorphic base type discriminated by type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonPolymorphic( - TypeDiscriminatorPropertyName = "status", + TypeDiscriminatorPropertyName = "type", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(McpOauthProbeResultNoAuthRequired), "no-auth-required")] -[JsonDerivedType(typeof(McpOauthProbeResultAuthenticated), "authenticated")] -[JsonDerivedType(typeof(McpOauthProbeResultNeedsAuth), "needs-auth")] -[JsonDerivedType(typeof(McpOauthProbeResultFailed), "failed")] -public partial class McpOauthProbeResult +[JsonDerivedType(typeof(TaskProgressAgent), "agent")] +[JsonDerivedType(typeof(TaskProgressClient), "client")] +[JsonDerivedType(typeof(TaskProgressShell), "shell")] +public partial class TaskProgress { /// The type discriminator. - [JsonPropertyName("status")] - public virtual string Status { get; set; } = string.Empty; + [JsonPropertyName("type")] + public virtual string Type { get; set; } = string.Empty; } -/// The no-auth-required variant of . +/// Timestamped display line for task progress output or recent agent activity. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpOauthProbeResultNoAuthRequired : McpOauthProbeResult +public sealed class TaskProgressLine { - /// - [JsonIgnore] - public override string Status => "no-auth-required"; + /// Display message, e.g., "▸ bash", "✓ edit src/foo.ts". + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; - /// HTTP response returned by the server. - [JsonPropertyName("httpResponse")] - public required McpOauthHttpResponse HttpResponse { get; set; } + /// ISO 8601 timestamp when this event occurred. + [JsonPropertyName("timestamp")] + public DateTimeOffset Timestamp { get; set; } } -/// The authenticated variant of . +/// Progress snapshot for an agent task, with recent activity lines and optional latest intent. +/// The agent variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpOauthProbeResultAuthenticated : McpOauthProbeResult +public partial class TaskProgressAgent : TaskProgress { /// [JsonIgnore] - public override string Status => "authenticated"; + public override string Type => "agent"; - /// HTTP response returned by the server. - [JsonPropertyName("httpResponse")] - public required McpOauthHttpResponse HttpResponse { get; set; } + /// The most recent intent reported by the agent. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("latestIntent")] + public string? LatestIntent { get; set; } + + /// Recent tool execution events converted to display lines. + [JsonPropertyName("recentActivity")] + public required IList RecentActivity { get; set; } } -/// The needs-auth variant of . +/// Generic progress for a client-owned task. +/// The client variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpOauthProbeResultNeedsAuth : McpOauthProbeResult +public partial class TaskProgressClient : TaskProgress { /// [JsonIgnore] - public override string Status => "needs-auth"; + public override string Type => "client"; - /// HTTP 401 or 403 response returned by the server. - [JsonPropertyName("httpResponse")] - public required McpOauthHttpResponse HttpResponse { get; set; } + /// Most recent nonempty progress message. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("lastMessage")] + public string? LastMessage { get; set; } - /// Why authentication is needed. - [JsonPropertyName("reason")] - public required McpOauthProbeNeedsAuthReason Reason { get; set; } + /// Current completion percentage from zero through one hundred. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("percentage")] + public double? Percentage { get; set; } - /// Parsed WWW-Authenticate challenge parameters, when present and parseable. + /// Current owner-defined progress phase. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("wwwAuthenticateParams")] - public McpOauthWWWAuthenticateParams? WwwAuthenticateParams { get; set; } + [JsonPropertyName("phase")] + public string? Phase { get; set; } + + /// Recent server-timestamped progress messages. + [JsonPropertyName("recentActivity")] + public required IList RecentActivity { get; set; } + + /// Sequence number of the latest accepted owner update. + [JsonPropertyName("sequence")] + public required long Sequence { get; set; } + + /// Current client task lifecycle status. + [JsonPropertyName("status")] + public required TaskClientStatus Status { get; set; } + + /// ISO 8601 timestamp of the latest accepted lifecycle change. + [JsonPropertyName("updatedAt")] + public required DateTimeOffset UpdatedAt { get; set; } } -/// The failed variant of . +/// Progress snapshot for a shell task, with recent stdout/stderr output and optional process ID. +/// The shell variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpOauthProbeResultFailed : McpOauthProbeResult +public partial class TaskProgressShell : TaskProgress { /// [JsonIgnore] - public override string Status => "failed"; - - /// Human-readable probe failure detail. - [JsonPropertyName("error")] - public required string Error { get; set; } + public override string Type => "shell"; - /// HTTP response returned by the server, when the probe reached the server and captured the complete response. + /// Process ID when available. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("httpResponse")] - public McpOauthHttpResponse? HttpResponse { get; set; } + [JsonPropertyName("pid")] + public long? Pid { get; set; } + + /// Recent stdout/stderr lines from the running shell command. + [JsonPropertyName("recentOutput")] + public required string RecentOutput { get; set; } } -/// Remote MCP server name for a passive OAuth status probe. +/// Progress information for the task, or null when no task with that ID is tracked. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpOauthProbeRequest +public sealed class TasksGetProgressResult { - /// Exact owned receipt identity; probing never activates a dormant installation. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } - - /// Name of the configured remote MCP server to probe. - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } + /// Progress information for the task, discriminated by type. Returns null when no task with this ID is currently tracked. + [JsonPropertyName("progress")] + public TaskProgress? Progress { get; set; } } -/// Remote MCP server name for a passive OAuth status probe. +/// Identifier of the background task to fetch progress for. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpOauthProbeRequestWithSession +internal sealed class TasksGetProgressRequest { - /// Exact owned receipt identity; probing never activates a dormant installation. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } - - /// Name of the configured remote MCP server to probe. - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// Task identifier (agent ID or shell ID). + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Honest terminal cancellation result; persistence or recovery failures remain RPC errors. +/// The first sync-waiting task that can currently be promoted to background mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionMcpOauthCancelLoginResult +public sealed class TasksGetCurrentPromotableResult { - /// True after cancellation settles, false when the original login already connected successfully. - [JsonPropertyName("cancelled")] - public bool Cancelled { get; set; } + /// The first sync-waiting task (agent first, then shell) that can currently be promoted to background mode. Omitted if no such task exists. The returned task is guaranteed to have executionMode='sync' and canPromoteToBackground=true at the time of the call. + [JsonPropertyName("task")] + public TaskInfo? Task { get; set; } } -/// Targets only the original prepared/applying owned login on this exact session requester. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMcpOauthCancelLoginRequest +internal sealed class SessionTasksGetCurrentPromotableRequest { - /// The same authoritative installation identity supplied during preparation. - [JsonPropertyName("expectedInstallationId")] - public string ExpectedInstallationId { get; set; } = string.Empty; - - /// Runtime-issued login handle known before the effectful login request begins. - [JsonPropertyName("loginId")] - public string LoginId { get; set; } = string.Empty; - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the pending MCP OAuth response was accepted. +/// Indicates whether the task was successfully promoted to background mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpOauthRespondResult +public sealed class TasksPromoteToBackgroundResult { - /// Whether the response was accepted. False if the request was unknown, timed out, or already resolved. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Whether the task was successfully promoted to background mode. + [JsonPropertyName("promoted")] + public bool Promoted { get; set; } } -/// Pending MCP OAuth request id to respond to. +/// Identifier of the task to promote to background mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpOauthRespondRequest +internal sealed class TasksPromoteToBackgroundRequest { - /// OAuth request identifier from the mcp.oauth_required event. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// Task identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the pending MCP headers refresh response was accepted. +/// The promoted task as it now exists in background mode, omitted if no promotable task was waiting. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpHeadersHandlePendingHeadersRefreshRequestResult +public sealed class TasksPromoteCurrentToBackgroundResult { - /// Whether the response was accepted. False if the request was unknown, timed out, or already resolved. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// The promoted task as it now exists in background mode, omitted if no promotable task was waiting. Atomic operation: avoids the race window of getCurrentPromotable + promoteToBackground. + [JsonPropertyName("task")] + public TaskInfo? Task { get; set; } } -/// Host response: supply dynamic headers or decline this refresh. -/// Polymorphic base type discriminated by kind. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(McpHeadersHandlePendingHeadersRefreshRequestHeaders), "headers")] -[JsonDerivedType(typeof(McpHeadersHandlePendingHeadersRefreshRequestNone), "none")] -[JsonDerivedType(typeof(McpHeadersHandlePendingHeadersRefreshRequestError), "error")] -public partial class McpHeadersHandlePendingHeadersRefreshRequest +internal sealed class SessionTasksPromoteCurrentToBackgroundRequest { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } - -/// The headers variant of . +/// Indicates whether the background task was successfully cancelled. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpHeadersHandlePendingHeadersRefreshRequestHeaders : McpHeadersHandlePendingHeadersRefreshRequest +public sealed class TasksCancelResult { - /// - [JsonIgnore] - public override string Kind => "headers"; - - /// Headers to overlay onto the MCP request. Dynamic headers override static config headers but do not replace SDK-managed request headers. - [JsonPropertyName("headers")] - public required IDictionary Headers { get; set; } - - /// Optional lifetime in milliseconds for these returned headers. The runtime clamps its configured cache lifetime to this value. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("ttlMs")] - public long? TtlMs { get; set; } + /// Whether the task was successfully cancelled. + [JsonPropertyName("cancelled")] + public bool Cancelled { get; set; } } -/// The none variant of . +/// Identifier of the background task to cancel. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpHeadersHandlePendingHeadersRefreshRequestNone : McpHeadersHandlePendingHeadersRefreshRequest +internal sealed class TasksCancelRequest { - /// - [JsonIgnore] - public override string Kind => "none"; + /// Task identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// The error variant of . +/// Indicates whether the task was removed. False when the task does not exist or is still running/idle. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpHeadersHandlePendingHeadersRefreshRequestError : McpHeadersHandlePendingHeadersRefreshRequest +public sealed class TasksRemoveResult { - /// - [JsonIgnore] - public override string Kind => "error"; - - /// Host credential broker failure, denial, or revocation reason. - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Whether the task was removed. Returns false if the task does not exist or is still running/idle (cancel it first). + [JsonPropertyName("removed")] + public bool Removed { get; set; } } -/// MCP headers refresh request id and the host response. +/// Identifier of the completed or cancelled task to remove from tracking. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpHeadersHandlePendingHeadersRefreshRequestRequest +internal sealed class TasksRemoveRequest { - /// Headers refresh request identifier from mcp.headers_refresh_required. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; - - /// Host response: supply dynamic headers or decline this refresh. - [JsonPropertyName("result")] - public McpHeadersHandlePendingHeadersRefreshRequest Result { get => field ??= new(); set; } + /// Task identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// MCP Apps resource content with URI, optional MIME type, text or base64 blob, and resource metadata. +/// Indicates whether the message was delivered, with an error message when delivery failed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAppsResourceContent +public sealed class TasksSendMessageResult { - /// Resource-level metadata (CSP, permissions, etc.). - [JsonPropertyName("_meta")] - public IDictionary? Meta { get; set; } - - /// Base64-encoded binary content. - [JsonPropertyName("blob")] - public string? Blob { get; set; } - - /// MIME type of the content. - [JsonPropertyName("mimeType")] - public string? MimeType { get; set; } - - /// Text content (e.g. HTML). - [JsonPropertyName("text")] - public string? Text { get; set; } + /// Error message if delivery failed. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// The resource URI (typically ui://...). - [JsonPropertyName("uri")] - public string Uri { get; set; } = string.Empty; + /// Whether the message was successfully delivered or steered. + [JsonPropertyName("sent")] + public bool Sent { get; set; } } -/// Resource contents returned by the MCP server. +/// Identifier of the target agent task, message content, and optional sender agent ID. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAppsReadResourceResult +internal sealed class TasksSendMessageRequest { - /// Resource contents returned by the server. - [JsonPropertyName("contents")] - public IList Contents { get => field ??= []; set; } + /// Agent ID of the sender, if sent on behalf of another agent. + [JsonPropertyName("fromAgentId")] + public string? FromAgentId { get; set; } + + /// Agent task identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Message content to send to the agent. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// MCP server and resource URI to fetch. +/// Skill metadata available to a session, with name, description, source, enabled/invocable state, path, plugin, and argument hint. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpAppsReadResourceRequest +public sealed class Skill { - /// Name of the MCP server hosting the resource. - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; - - /// Resource URI (typically ui://...). - [JsonPropertyName("uri")] - public string Uri { get; set; } = string.Empty; -} - -/// App-callable tools from the named MCP server. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAppsListToolsResult -{ - /// App-callable tools from the server. - [JsonPropertyName("tools")] - public IList> Tools { get => field ??= []; set; } -} + /// Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field. + [JsonPropertyName("argumentHint")] + public string? ArgumentHint { get; set; } -/// MCP server to list app-callable tools for. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpAppsListToolsRequest -{ - /// **Required.** Server whose ui:// view issued the request. Per SEP-1865 ('callable by the app from this server only'), the call is rejected when this differs from `serverName`, and rejected outright when missing. - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("originServerName")] - public string OriginServerName { get; set; } = string.Empty; + /// Canonical slash command name used to invoke the skill, without the leading '/'. + [JsonPropertyName("commandName")] + public string? CommandName { get; set; } - /// MCP server hosting the app. - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// Description of what the skill does. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Whether the skill is currently enabled. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } -/// MCP server, tool name, and arguments to invoke from an MCP App view. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpAppsCallToolRequest -{ - /// Tool arguments. - [JsonPropertyName("arguments")] - public IDictionary? Arguments { get; set; } + /// Unique identifier for the skill. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// **Required.** Server whose ui:// view issued the request. Per SEP-1865 ('callable by the app from this server only'), the call is rejected when this differs from `serverName`, and rejected outright when missing. - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("originServerName")] - public string OriginServerName { get; set; } = string.Empty; + /// Absolute path to the skill file. + [JsonPropertyName("path")] + public string? Path { get; set; } - /// MCP server hosting the tool. - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// Name of the plugin that provides the skill, when source is 'plugin'. + [JsonPropertyName("pluginName")] + public string? PluginName { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Source location type (e.g., project, personal-copilot, plugin, builtin). + [JsonPropertyName("source")] + public SkillSource Source { get; set; } - /// MCP tool name. - [JsonPropertyName("toolName")] - public string ToolName { get; set; } = string.Empty; + /// Whether the skill can be invoked by the user as a slash command. + [JsonPropertyName("userInvocable")] + public bool UserInvocable { get; set; } } -/// Host context advertised to MCP App guests. +/// Skills available to the session, with their enabled state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAppsSetHostContextDetails +public sealed class SkillList { - /// Display modes the host supports. - [JsonPropertyName("availableDisplayModes")] - public IList? AvailableDisplayModes { get; set; } - - /// Current display mode (SEP-1865). - [JsonPropertyName("displayMode")] - public McpAppsSetHostContextDetailsDisplayMode? DisplayMode { get; set; } - - /// BCP-47 locale, e.g. 'en-US'. - [JsonPropertyName("locale")] - public string? Locale { get; set; } - - /// Platform type for responsive design. - [JsonPropertyName("platform")] - public McpAppsSetHostContextDetailsPlatform? Platform { get; set; } - - /// UI theme preference per SEP-1865. - [JsonPropertyName("theme")] - public McpAppsSetHostContextDetailsTheme? Theme { get; set; } - - /// IANA timezone, e.g. 'America/New_York'. - [JsonPropertyName("timeZone")] - public string? TimeZone { get; set; } - - /// Host application identifier. - [JsonPropertyName("userAgent")] - public string? UserAgent { get; set; } + /// Available skills. + [JsonPropertyName("skills")] + public IList Skills { get => field ??= []; set; } } -/// Host context to advertise to MCP App guests. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpAppsSetHostContextRequest +internal sealed class SessionSkillsListRequest { - /// Host context advertised to MCP App guests. - [JsonPropertyName("context")] - public McpAppsSetHostContextDetails Context { get => field ??= new(); set; } - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Current host context. +/// Skill invocation record with name, path, content, allowed tools, and turn number. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAppsHostContextDetails +public sealed class SkillsInvokedSkill { - /// Display modes the host supports. - [JsonPropertyName("availableDisplayModes")] - public IList? AvailableDisplayModes { get; set; } - - /// Current display mode (SEP-1865). - [JsonPropertyName("displayMode")] - public McpAppsHostContextDetailsDisplayMode? DisplayMode { get; set; } + /// Tools that should be auto-approved when this skill is active, captured at invocation time. + [JsonPropertyName("allowedTools")] + public IList? AllowedTools { get; set; } - /// BCP-47 locale, e.g. 'en-US'. - [JsonPropertyName("locale")] - public string? Locale { get; set; } + /// Full content of the skill file. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; - /// Platform type for responsive design. - [JsonPropertyName("platform")] - public McpAppsHostContextDetailsPlatform? Platform { get; set; } + /// Whether model invocation was disabled when this skill was invoked. + [JsonPropertyName("disableModelInvocation")] + public bool? DisableModelInvocation { get; set; } - /// UI theme preference per SEP-1865. - [JsonPropertyName("theme")] - public McpAppsHostContextDetailsTheme? Theme { get; set; } + /// Turn number when the skill was invoked. + [JsonPropertyName("invokedAtTurn")] + public long InvokedAtTurn { get; set; } - /// IANA timezone, e.g. 'America/New_York'. - [JsonPropertyName("timeZone")] - public string? TimeZone { get; set; } + /// Unique identifier for the skill. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Host application identifier. - [JsonPropertyName("userAgent")] - public string? UserAgent { get; set; } + /// Path to the SKILL.md file, or an empty string for an SDK-provided skill without a filesystem identity. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; } -/// Current host context advertised to MCP App guests. +/// Skills invoked during this session, ordered by invocation time (most recent last). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAppsHostContext +public sealed class SkillsGetInvokedResult { - /// Current host context. - [JsonPropertyName("context")] - public McpAppsHostContextDetails Context { get => field ??= new(); set; } + /// Skills invoked during this session, ordered by invocation time (most recent last). + [JsonPropertyName("skills")] + public IList Skills { get => field ??= []; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMcpAppsGetHostContextRequest +internal sealed class SessionSkillsGetInvokedRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Capability negotiation snapshot. +/// Name of the skill to enable for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAppsDiagnoseCapability +internal sealed class SkillsEnableRequest { - /// Whether the runtime advertises `extensions.io.modelcontextprotocol/ui` to MCP servers. - [JsonPropertyName("advertised")] - public bool Advertised { get; set; } - - /// Whether the MCP_APPS feature flag (or COPILOT_MCP_APPS env override) is on. - [JsonPropertyName("featureFlagEnabled")] - public bool FeatureFlagEnabled { get; set; } + /// Name of the skill to enable. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Whether the session has the `mcp-apps` capability. - [JsonPropertyName("sessionHasMcpApps")] - public bool SessionHasMcpApps { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// What the server returned for this session. +/// Name of the skill to disable for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAppsDiagnoseServer +internal sealed class SkillsDisableRequest { - /// Whether the named server is currently connected. - [JsonPropertyName("connected")] - public bool Connected { get; set; } - - /// Up to 5 tool names with `_meta.ui` for quick inspection. - [JsonPropertyName("sampleToolNames")] - public IList SampleToolNames { get => field ??= []; set; } - - /// Total tools returned by the server's tools/list. - [JsonPropertyName("toolCount")] - public double ToolCount { get; set; } + /// Name of the skill to disable. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Tools whose `_meta.ui` is populated (resourceUri and/or visibility set). - [JsonPropertyName("toolsWithUiMeta")] - public double ToolsWithUiMeta { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Diagnostic snapshot of MCP Apps wiring for the named server. +/// Diagnostics from reloading skill definitions, with warnings and errors as separate lists. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAppsDiagnoseResult +public sealed class SkillsLoadDiagnostics { - /// Capability negotiation snapshot. - [JsonPropertyName("capability")] - public McpAppsDiagnoseCapability Capability { get => field ??= new(); set; } + /// Errors emitted while loading skills (e.g. skills that failed to load entirely). + [JsonPropertyName("errors")] + public IList Errors { get => field ??= []; set; } - /// What the server returned for this session. - [JsonPropertyName("server")] - public McpAppsDiagnoseServer Server { get => field ??= new(); set; } + /// Warnings emitted while loading skills (e.g. skills that loaded but had issues). + [JsonPropertyName("warnings")] + public IList Warnings { get => field ??= []; set; } } -/// MCP server to diagnose MCP Apps wiring for. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpAppsDiagnoseRequest +internal sealed class SessionSkillsReloadRequest { - /// MCP server to probe. - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// MCP resource content with URI, optional MIME type, text or base64 blob, and resource metadata. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpResourceContent +internal sealed class SessionSkillsEnsureLoadedRequest { - /// Resource-level metadata (CSP, permissions, etc.). - [JsonPropertyName("_meta")] - public IDictionary? Meta { get; set; } - - /// Base64-encoded binary content. - [JsonPropertyName("blob")] - public string? Blob { get; set; } - - /// MIME type of the content. - [JsonPropertyName("mimeType")] - public string? MimeType { get; set; } - - /// Text content (e.g. HTML). - [JsonPropertyName("text")] - public string? Text { get; set; } - - /// The resource URI. - [JsonPropertyName("uri")] - public string Uri { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Resource contents returned by the MCP server. +/// The IDE a host is connected to, as reported to the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpResourcesReadResult +public sealed class SessionConnectedIdeInfo { - /// Resource contents returned by the server. - [JsonPropertyName("contents")] - public IList Contents { get => field ??= []; set; } + /// Display name of the connected IDE, for example `VS Code`. + [JsonPropertyName("ideName")] + public string IdeName { get; set; } = string.Empty; + + /// Absolute path of the workspace folder the IDE has open. + [JsonPropertyName("workspaceFolder")] + public string WorkspaceFolder { get; set; } = string.Empty; } -/// MCP server and resource URI to fetch. +/// Records which IDE the host is connected to, or clears it. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpResourcesReadRequest +internal sealed class SessionMcpSetConnectedIdeInfoParams { - /// Name of the MCP server hosting the resource. - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// The connected IDE. Null or omitted clears the recorded IDE, which is how a host reports that it is disconnected. + [JsonPropertyName("ide")] + public SessionConnectedIdeInfo? Ide { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// Resource URI. - [JsonPropertyName("uri")] - public string Uri { get; set; } = string.Empty; } -/// Standard MCP resource annotations plus preserved non-standard annotation fields. +/// Recorded MCP server connection failure. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpResourceAnnotations +public sealed class McpServerFailureInfo { - /// Server-provided non-standard annotation fields preserved from the MCP response. - [JsonPropertyName("additionalProperties")] - public IDictionary? AdditionalProperties { get; set; } - - /// Intended audience roles for this resource. - [JsonPropertyName("audience")] - public IList? Audience { get; set; } + /// Failure message produced when the MCP server connection failed. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; - /// Last-modified timestamp hint. - [JsonPropertyName("lastModified")] - public string? LastModified { get; set; } + /// epoch-ms timestamp at which the failure was recorded. + [JsonPropertyName("timestamp")] + public long Timestamp { get; set; } +} - /// Priority hint for model/client use. - [JsonPropertyName("priority")] - public double? Priority { get; set; } +/// Recorded MCP server pending-auth state. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpServerNeedsAuthInfo +{ + /// epoch-ms timestamp at which the server signalled it needs authentication. + [JsonPropertyName("timestamp")] + public long Timestamp { get; set; } } -/// A resource icon descriptor plus preserved non-standard icon fields. +/// Host-level state, omitted when no MCP host is initialized. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpResourceIcon +public sealed class McpHostState { - /// Server-provided non-standard icon fields preserved from the MCP response. - [JsonPropertyName("additionalProperties")] - public IDictionary? AdditionalProperties { get; set; } + /// Names of currently-connected MCP clients. + [JsonPropertyName("clients")] + public IList Clients { get => field ??= []; set; } - /// Icon MIME type, when known. - [JsonPropertyName("mimeType")] - public string? MimeType { get; set; } + /// Configured servers that are explicitly disabled. + [JsonPropertyName("disabledServers")] + public IList DisabledServers { get => field ??= []; set; } - /// Icon sizes hint. - [JsonPropertyName("sizes")] - public string? Sizes { get; set; } + /// Map of server name to recorded connection failure. + [JsonPropertyName("failedServers")] + public IDictionary FailedServers { get => field ??= new Dictionary(); set; } - /// Icon URI. - [JsonPropertyName("src")] - public string Src { get; set; } = string.Empty; + /// Configured servers filtered out by MCP server policy. + [JsonPropertyName("filteredServers")] + public IList FilteredServers { get => field ??= []; set; } - /// Theme hint for this icon. - [JsonPropertyName("theme")] - public string? Theme { get; set; } + /// Whether third-party MCP servers are policy-enabled for this session. + [JsonPropertyName("mcp3pEnabled")] + public bool Mcp3pEnabled { get; set; } + + /// Map of server name to recorded pending-auth state. + [JsonPropertyName("needsAuthServers")] + public IDictionary NeedsAuthServers { get => field ??= new Dictionary(); set; } + + /// Names of servers with in-flight connection attempts. + [JsonPropertyName("pendingConnections")] + public IList PendingConnections { get => field ??= []; set; } } -/// An MCP resource descriptor (spec `Resource`): URI, name, and optional title, description, MIME type, size, icons, annotations, and metadata. Server-provided fields outside the standard descriptor shape are exposed under `additionalProperties`. +/// Owned installation that a listed MCP server's live configuration came from. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpResource +public sealed class McpServerOwnership { - /// Resource-level metadata. - [JsonPropertyName("_meta")] - public IDictionary? Meta { get; set; } + /// Stable installation identifier from the owned installation receipt. + [JsonPropertyName("installationId")] + public string InstallationId { get; set; } = string.Empty; +} - /// Server-provided non-standard descriptor fields preserved from the MCP response. - [JsonPropertyName("additionalProperties")] - public IDictionary? AdditionalProperties { get; set; } +/// MCP server status entry, including config source/plugin source and any connection error. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpServer +{ + /// Human-readable display name supplied by a managed server catalog. + [JsonPropertyName("displayName")] + public string? DisplayName { get; set; } - /// Model/client annotations associated with this resource. - [JsonPropertyName("annotations")] - public McpResourceAnnotations? Annotations { get; set; } + /// Error message if the server failed to connect. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Optional description of what this resource represents. - [JsonPropertyName("description")] - public string? Description { get; set; } + /// Server name (config key). + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Icons associated with this resource. - [JsonPropertyName("icons")] - public IList? Icons { get; set; } + /// Owned installation this entry's live configuration came from. Absent for manual, workspace, plugin, builtin and same-name servers, and on runtimes without owned installations. + [JsonPropertyName("owned")] + public McpServerOwnership? Owned { get; set; } - /// MIME type of the resource, if known. - [JsonPropertyName("mimeType")] - public string? MimeType { get; set; } + /// Server-advertised metadata for a connected server. Omitted when no live connection metadata is available, including while pending or when failed, disabled, stopped, or not configured. + [JsonPropertyName("serverMetadata")] + public McpServerMetadata? ServerMetadata { get; set; } - /// The programmatic name of the resource. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Configuration source: user, workspace, plugin, builtin, or managed. + [JsonPropertyName("source")] + public McpServerSource? Source { get; set; } - /// Resource size in bytes, when known. - [JsonPropertyName("size")] - public long? Size { get; set; } + /// Plugin name that provided this server, when source is plugin. + [JsonPropertyName("sourcePlugin")] + public string? SourcePlugin { get; set; } - /// Optional human-readable display title. - [JsonPropertyName("title")] - public string? Title { get; set; } + /// Plugin version that provided this server, when source is plugin. + [JsonPropertyName("sourcePluginVersion")] + public string? SourcePluginVersion { get; set; } - /// The resource URI (e.g. ui://... or file:///...). - [JsonPropertyName("uri")] - public string Uri { get; set; } = string.Empty; + /// Connection status: connected, failed, needs-auth, pending, disabled, stopped, or not_configured. + [JsonPropertyName("status")] + public McpServerStatus Status { get; set; } + + /// Configured URL for an HTTP/SSE server, regardless of configuration source. Omitted for local and in-memory servers. + [JsonPropertyName("url")] + public string? Url { get; set; } } -/// One page of resources advertised by the named MCP server. +/// MCP servers configured for the session, with their connection status and host-level state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpResourcesListResult +public sealed class McpServerList { - /// Opaque cursor for the next page, if the server has more resources. - [JsonPropertyName("nextCursor")] - public string? NextCursor { get; set; } + /// Host-level state, omitted when no MCP host is initialized. + [JsonPropertyName("host")] + public McpHostState? Host { get; set; } - /// Resources advertised by the server (proxied MCP `resources/list`). - [JsonPropertyName("resources")] - public IList Resources { get => field ??= []; set; } + /// Configured MCP servers. + [JsonPropertyName("servers")] + public IList Servers { get => field ??= []; set; } } -/// MCP server whose resources to enumerate. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpResourcesListRequest +internal sealed class SessionMcpListRequest { - /// Opaque MCP pagination cursor from a prior `nextCursor` value. - [JsonPropertyName("cursor")] - public string? Cursor { get; set; } - - /// Name of the MCP server whose resources to enumerate. - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// An MCP resource template descriptor (spec `ResourceTemplate`): an RFC 6570 URI template, name, and optional title, description, MIME type, icons, annotations, and metadata. Server-provided fields outside the standard descriptor shape are exposed under `additionalProperties`. +/// Observational state for a matching already materialized MCP server. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpResourceTemplate +public sealed class McpConfiguredServerState { - /// Resource-template-level metadata. - [JsonPropertyName("_meta")] - public IDictionary? Meta { get; set; } + /// Observed connection error, when the materialized server failed. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Server-provided non-standard descriptor fields preserved from the MCP response. - [JsonPropertyName("additionalProperties")] - public IDictionary? AdditionalProperties { get; set; } - - /// Model/client annotations associated with this template. - [JsonPropertyName("annotations")] - public McpResourceAnnotations? Annotations { get; set; } + /// Observed connection status. This is not a configuration or readiness guarantee. + [JsonPropertyName("status")] + public McpServerStatus Status { get; set; } +} - /// Optional description of what this template is for. - [JsonPropertyName("description")] - public string? Description { get; set; } +/// Effective MCP configuration entry. Configuration enablement is distinct from the optional live observation. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpConfiguredServer +{ + /// Human-readable display name supplied by configuration. + [JsonPropertyName("displayName")] + public string? DisplayName { get; set; } - /// Icons associated with resources matching this template. - [JsonPropertyName("icons")] - public IList? Icons { get; set; } + /// Whether this configured server is enabled after session configuration and policy filtering. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } - /// MIME type for resources matching this template, if uniform. - [JsonPropertyName("mimeType")] - public string? MimeType { get; set; } + /// Observed state from an already materialized matching server. Omitted when no live graph has this configured server; it never determines configuration enablement. + [JsonPropertyName("live")] + public McpConfiguredServerState Live { get => field ??= new(); set; } - /// The programmatic name of the resource template. + /// Server name (config key). + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; - /// Optional human-readable display title. - [JsonPropertyName("title")] - public string? Title { get; set; } + /// Configuration provenance: user, workspace, plugin, builtin, or managed. + [JsonPropertyName("source")] + public McpServerSource? Source { get; set; } - /// An RFC 6570 URI template for constructing resource URIs. - [JsonPropertyName("uriTemplate")] - public string UriTemplate { get; set; } = string.Empty; + /// Plugin name that provided this server, when source is plugin. + [JsonPropertyName("sourcePlugin")] + public string? SourcePlugin { get; set; } + + /// Plugin version that provided this server, when source is plugin. + [JsonPropertyName("sourcePluginVersion")] + public string? SourcePluginVersion { get; set; } } -/// One page of resource templates advertised by the named MCP server. +/// Effective MCP configuration with optional live observations from matching already materialized servers. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpResourcesListTemplatesResult +public sealed class McpConfiguredServerList { - /// Opaque cursor for the next page, if the server has more resource templates. - [JsonPropertyName("nextCursor")] - public string? NextCursor { get; set; } - - /// Resource templates advertised by the server (proxied MCP `resources/templates/list`). - [JsonPropertyName("resourceTemplates")] - public IList ResourceTemplates { get => field ??= []; set; } + /// Effective configured MCP servers. + [JsonPropertyName("servers")] + public IList Servers { get => field ??= []; set; } } -/// MCP server whose resource templates to enumerate. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpResourcesListTemplatesRequest +internal sealed class SessionMcpListConfiguredRequest { - /// Opaque MCP pagination cursor from a prior `nextCursor` value. - [JsonPropertyName("cursor")] - public string? Cursor { get; set; } - - /// Name of the MCP server whose resource templates to enumerate. - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// An argument accepted by an MCP prompt. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpPromptArgument -{ - /// Argument-level metadata. - [JsonPropertyName("_meta")] - public IDictionary? Meta { get; set; } - - /// Server-provided non-standard argument fields. - [JsonPropertyName("additionalProperties")] - public IDictionary? AdditionalProperties { get; set; } - - /// Description of the argument. - [JsonPropertyName("description")] - public string? Description { get; set; } - - /// Name of the argument. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - /// Whether the argument is required; omission is distinct from false. - [JsonPropertyName("required")] - public bool? Required { get; set; } -} - -/// An MCP prompt icon with standard size hints and preserved non-standard fields. +/// Normalized MCP Apps discovery metadata from a tool's `_meta.ui` block. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpPromptIcon +public sealed class McpToolUi { - /// Server-provided non-standard icon fields. - [JsonPropertyName("additionalProperties")] - public IDictionary? AdditionalProperties { get; set; } - - /// Icon MIME type, when known. - [JsonPropertyName("mimeType")] - public string? MimeType { get; set; } - - /// Icon sizes, such as `48x48` or `any`. - [JsonPropertyName("sizes")] - public IList? Sizes { get; set; } - - /// Icon URI. - [JsonPropertyName("src")] - public string Src { get; set; } = string.Empty; + /// URI of the tool's MCP App resource, typically a `ui://` resource identifier. Use `session.mcp.resources.read` to fetch its HTML and resource metadata. + [JsonPropertyName("resourceUri")] + public string? ResourceUri { get; set; } - /// Theme hint for this icon. - [JsonPropertyName("theme")] - public string? Theme { get; set; } + /// Tool visibility advertised by the server. When absent, MCP Apps defaults apply. + [JsonPropertyName("visibility")] + public IList? Visibility { get; set; } } -/// An MCP prompt descriptor. Server-provided non-standard fields are exposed under `additionalProperties`. +/// MCP tool metadata with tool name, optional description, and normalized MCP Apps discovery metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpPrompt +public sealed class McpTools { - /// Prompt-level metadata. - [JsonPropertyName("_meta")] - public IDictionary? Meta { get; set; } - - /// Server-provided non-standard descriptor fields. - [JsonPropertyName("additionalProperties")] - public IDictionary? AdditionalProperties { get; set; } - - /// Arguments accepted by the prompt. - [JsonPropertyName("arguments")] - public IList? Arguments { get; set; } - - /// Description of what this prompt provides. + /// Tool description, when provided. [JsonPropertyName("description")] public string? Description { get; set; } - /// Icons associated with this prompt. - [JsonPropertyName("icons")] - public IList? Icons { get; set; } - - /// The programmatic name of the prompt. + /// Tool name. [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; - /// Human-readable display title. - [JsonPropertyName("title")] - public string? Title { get; set; } + /// Normalized MCP Apps discovery metadata. An empty object indicates that a valid `_meta.ui` block was present without recognized fields. + [JsonPropertyName("ui")] + public McpToolUi? Ui { get; set; } } -/// One page of prompts advertised by the named MCP server. +/// Tools exposed by the connected MCP server. Throws when the server is not connected. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpPromptsListResult +public sealed class McpListToolsResult { - /// MCP result metadata. - [JsonPropertyName("_meta")] - public IDictionary? Meta { get; set; } - - /// Server-provided non-standard result fields. - [JsonPropertyName("additionalProperties")] - public IDictionary? AdditionalProperties { get; set; } - - /// Opaque cursor for the next page, if the server has more prompts. - [JsonPropertyName("nextCursor")] - public string? NextCursor { get; set; } - - /// Prompts advertised by the server. - [JsonPropertyName("prompts")] - public IList Prompts { get => field ??= []; set; } + /// Tools exposed by the server. + [JsonPropertyName("tools")] + public IList Tools { get => field ??= []; set; } } -/// MCP server whose prompts to enumerate. +/// Server name whose tool list should be returned. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpPromptsListRequest +internal sealed class McpListToolsRequest { - /// Opaque MCP pagination cursor from a prior `nextCursor` value. - [JsonPropertyName("cursor")] - public string? Cursor { get; set; } - - /// Name of the MCP server whose prompts to enumerate. + /// Name of the connected MCP server whose tools to list. [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] [MinLength(1)] @@ -18507,63 +18995,67 @@ internal sealed class McpPromptsListRequest public string SessionId { get; set; } = string.Empty; } -/// An MCP prompt message with opaque JSON content preserved without flattening or content-type filtering. +/// Name of the MCP server to enable for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpPromptMessage +public sealed class McpEnableRequest { - /// Message-level metadata. - [JsonPropertyName("_meta")] - public IDictionary? Meta { get; set; } - - /// Server-provided non-standard message fields. - [JsonPropertyName("additionalProperties")] - public IDictionary? AdditionalProperties { get; set; } - - /// The original MCP content block, including nested metadata and unfamiliar content types. - [JsonPropertyName("content")] - public JsonElement Content { get; set; } + /// Exact receipt identity for explicit owned activation in this session. + [JsonPropertyName("expectedInstallationId")] + public string? ExpectedInstallationId { get; set; } - /// The role of the message sender. - [JsonPropertyName("role")] - public McpPromptRole Role { get; set; } + /// Name of the MCP server to enable. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } } -/// Prompt messages returned by the MCP server without sending them to the model. +/// Name of the MCP server to enable for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpPromptsGetResult +internal sealed class McpEnableRequestWithSession { - /// MCP result metadata. - [JsonPropertyName("_meta")] - public IDictionary? Meta { get; set; } - - /// Server-provided non-standard result fields. - [JsonPropertyName("additionalProperties")] - public IDictionary? AdditionalProperties { get; set; } + /// Exact receipt identity for explicit owned activation in this session. + [JsonPropertyName("expectedInstallationId")] + public string? ExpectedInstallationId { get; set; } - /// Description of the prompt. - [JsonPropertyName("description")] - public string? Description { get; set; } + /// Name of the MCP server to enable. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; - /// Ordered prompt messages. - [JsonPropertyName("messages")] - public IList Messages { get => field ??= []; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// MCP server, prompt name, and optional string-valued arguments. +/// Name of the MCP server to disable for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpPromptsGetRequest +public sealed class McpDisableRequest { - /// String-valued arguments to pass to the prompt. - [JsonPropertyName("arguments")] - public IDictionary? Arguments { get; set; } + /// Required for an owned installation; omission preserves only manual-server behaviour. + [JsonPropertyName("expectedInstallationId")] + public string? ExpectedInstallationId { get; set; } - /// The programmatic name of the prompt. + /// Name of the MCP server to disable. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] [MinLength(1)] - [JsonPropertyName("promptName")] - public string PromptName { get; set; } = string.Empty; + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } +} - /// Name of the MCP server hosting the prompt. +/// Name of the MCP server to disable for the session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpDisableRequestWithSession +{ + /// Required for an owned installation; omission preserves only manual-server behaviour. + [JsonPropertyName("expectedInstallationId")] + public string? ExpectedInstallationId { get; set; } + + /// Name of the MCP server to disable. [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] [MinLength(1)] @@ -18575,564 +19067,3355 @@ internal sealed class McpPromptsGetRequest public string SessionId { get; set; } = string.Empty; } -/// MCP diagnostic source configuration. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpDiagnosticSourceConfiguration +internal sealed class SessionMcpReloadRequest { - /// Threshold to apply to MCP diagnostic producers in this session. - [JsonPropertyName("level")] - public DiagnosticLogLevel Level { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Typed diagnostic source configuration. At least one source is required by diagnostics configuration methods. +/// Result of moving in-flight MCP loading to the background. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DiagnosticSourcesConfiguration +public sealed class MoveMcpLoadingToBackgroundResult { - /// MCP diagnostic capture threshold. Omit to leave the current threshold unchanged. - [JsonPropertyName("mcp")] - public McpDiagnosticSourceConfiguration? Mcp { get; set; } + /// Whether an in-flight MCP load was moved to the background, releasing turns that were waiting on it. False when no MCP load was in flight or the waiting turns had already been released. + [JsonPropertyName("movedToBackground")] + public bool MovedToBackground { get; set; } } -/// Per-source session diagnostics configuration. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DiagnosticsConfiguration +internal sealed class SessionMcpMoveLoadingToBackgroundRequest { - /// Diagnostic thresholds keyed by supported source. - [JsonPropertyName("sources")] - public DiagnosticSourcesConfiguration Sources { get => field ??= new(); set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Patch session diagnostic thresholds for explicitly supplied sources. +/// MCP server allowed by policy, with server name and optional PII-free explanatory note. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class DiagnosticsConfigureRequest +public sealed class McpAllowedServer { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Allowed server name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Sources to configure. At least one supported source must be supplied. - [JsonPropertyName("sources")] - public DiagnosticSourcesConfiguration Sources { get => field ??= new(); set; } + /// PII-free note explaining why the server was allowed. + [JsonPropertyName("redactedNote")] + public string? RedactedNote { get; set; } } -/// One retained session-scoped diagnostic record. Potentially content-bearing diagnostic data is opt-in and must not be exported automatically as telemetry. -/// Polymorphic base type discriminated by source. +/// MCP server whose connection attempt failed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "source", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(DiagnosticEntryMcp), "mcp")] -public partial class DiagnosticEntry +public sealed class McpFailedServer { - /// The type discriminator. - [JsonPropertyName("source")] - public virtual string Source { get; set; } = string.Empty; -} + /// The captured connection failure detail. + [JsonPropertyName("error")] + public string? Error { get; set; } + /// The config key of the server that failed to connect. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; +} -/// MCP-specific detail for a source-discriminated diagnostic entry. +/// MCP server filtered by policy, with name, reason, and optional redacted reason. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpDiagnosticDetails +public sealed class McpFilteredServer { - /// Fresh identifier for the MCP connection attempt, including failed starts. - [JsonPropertyName("connectionId")] - public string ConnectionId { get; set; } = string.Empty; - - /// Serialized diagnostic detail. Protocol and HTTP records use JSON when detail is present. - [JsonPropertyName("data")] - public string? Data { get; set; } + /// Deprecated. This field is no longer populated. + [EditorBrowsable(EditorBrowsableState.Never)] +#if NET5_0_OR_GREATER + [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] +#else + [Obsolete("This member is deprecated and will be removed in a future version.")] +#endif + [JsonPropertyName("enterpriseName")] + public string? EnterpriseName { get; set; } - /// Protocol-frame direction when kind is protocol. - [JsonPropertyName("direction")] - public McpDiagnosticDirection? Direction { get; set; } + /// Filtered server name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Diagnostic record category. - [JsonPropertyName("kind")] - public McpDiagnosticKind Kind { get; set; } + /// Human-readable filter reason. + [JsonPropertyName("reason")] + public string Reason { get; set; } = string.Empty; - /// Configured MCP server name. - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// PII-free filter reason. + [JsonPropertyName("redactedReason")] + public string? RedactedReason { get; set; } } -/// The mcp variant of . +/// MCP server startup filtering result. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class DiagnosticEntryMcp : DiagnosticEntry +internal sealed class McpStartServersResult { - /// - [JsonIgnore] - public override string Source => "mcp"; - - /// Agent identifier for a subagent host. Omitted for the root agent. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("agentId")] - public string? AgentId { get; set; } + /// Non-default servers allowed by policy. + [JsonPropertyName("allowedServers")] + public IList? AllowedServers { get; set; } - /// Typed MCP diagnostic detail. - [JsonPropertyName("details")] - public required McpDiagnosticDetails Details { get; set; } + /// Servers whose connection attempt failed. + [JsonPropertyName("failedServers")] + public IList? FailedServers { get; set; } - /// Severity of this emitted diagnostic record. - [JsonPropertyName("level")] - public required DiagnosticSeverity Level { get; set; } + /// Servers filtered out before startup. + [JsonPropertyName("filteredServers")] + public IList FilteredServers { get => field ??= []; set; } +} - /// Human-readable diagnostic summary. - [JsonPropertyName("message")] - public required string Message { get; set; } +/// Opaque MCP reload configuration. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpReloadWithConfigRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Original byte count when a known-size message or data value was truncated. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("originalBytes")] - public long? OriginalBytes { get; set; } +/// Outcome of an MCP sampling execution: success result, failure error, or cancellation. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpSamplingExecutionResult +{ + /// Outcome of the sampling inference. 'success' produced a response; 'failure' encountered an error (including agent-side rejection by content filter or criteria); 'cancelled' the caller cancelled this execution via cancelSamplingExecution. + [JsonPropertyName("action")] + public McpSamplingExecutionAction Action { get; set; } - /// UTC RFC 3339 timestamp captured at the diagnostic source. - [JsonPropertyName("timestamp")] - public required string Timestamp { get; set; } + /// Error description, present when action='failure'. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Whether message or data was truncated to the record-size bound. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("truncated")] - public bool? Truncated { get; set; } + /// MCP CreateMessageResult payload (with optional 'tools' extension), present when action='success'. Treated as opaque at the schema layer; consumers should construct/consume it per the MCP CreateMessageResult shape. + [JsonPropertyName("result")] + public JsonElement? Result { get; set; } } -/// One cursor-addressed page of retained session diagnostics. +/// Identifiers and raw MCP CreateMessageRequest params used to run a sampling inference. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DiagnosticsReadResult +internal sealed class McpExecuteSamplingParams { - /// Opaque cursor for the next independent read. - [JsonPropertyName("cursor")] - public string Cursor { get; set; } = string.Empty; + /// The original MCP JSON-RPC request ID (string or number). Used by the runtime to correlate the inference with the originating MCP request for telemetry; this is distinct from `requestId` (which is the schema-level cancellation handle). + [JsonPropertyName("mcpRequestId")] + public JsonElement McpRequestId { get; set; } - /// Whether the requested cursor remained within the retained buffer window. - [JsonPropertyName("cursorStatus")] - public DiagnosticCursorStatus CursorStatus { get; set; } + /// Raw MCP CreateMessageRequest params, as received in the `sampling.requested` event. Treated as opaque at the schema layer; the runtime converts the embedded MCP messages into the OpenAI chat-completion shape internally. + [JsonPropertyName("request")] + public JsonElement Request { get; set; } - /// Number of records lost before this page when known. Omitted when a buffer generation change makes the count unknowable. - [JsonPropertyName("droppedCount")] - public long? DroppedCount { get; set; } + /// Caller-provided unique identifier for this sampling execution. Use this same ID with cancelSamplingExecution to cancel the in-flight call. Must be unique within the session for the lifetime of the call. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; - /// Retained records beginning at the requested cursor. - [JsonPropertyName("entries")] - public IList Entries { get => field ??= []; set; } + /// Name of the MCP server that initiated the sampling request. + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; - /// Whether additional retained records follow this page. - [JsonPropertyName("hasMore")] - public bool HasMore { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Cursor-based request for session diagnostics. The default limit is 100 (maximum 500); the default waitMs is zero (maximum 30000). +/// Indicates whether an in-flight sampling execution with the given requestId was found and cancelled. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class DiagnosticsReadRequest +public sealed class McpCancelSamplingExecutionResult { - /// Opaque cursor returned by an earlier read. Omit to start at the oldest retained record. - [JsonPropertyName("cursor")] - public string? Cursor { get; set; } + /// True if an in-flight execution with the given requestId was found and signalled to cancel. False when no such execution is in flight (already completed, never started, or cancelled by another caller). + [JsonPropertyName("cancelled")] + public bool Cancelled { get; set; } +} - /// Maximum number of records to return, from 1 through 500. Omit for 100. - [JsonPropertyName("max")] - public long? Max { get; set; } +/// The requestId previously passed to executeSampling that should be cancelled. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpCancelSamplingExecutionParams +{ + /// The requestId previously passed to executeSampling that should be cancelled. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// Nonempty selection of sources to read. Each source may be listed once. - [JsonPropertyName("sources")] - public IList Sources { get => field ??= []; set; } - - /// Maximum time in milliseconds to wait for a new record, from 0 through 30000. - [JsonPropertyName("waitMs")] - public int? WaitMs { get; set; } } -/// Feature detection and hard polling limits for the EXPERIMENTAL session connector API. +/// Env-value mode recorded on the session after the update. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorCapabilities +public sealed class McpSetEnvValueModeResult { - /// Connector API contract version. - [JsonPropertyName("apiVersion")] - public long ApiVersion { get; set; } + /// Mode recorded on the session after the update. + [JsonPropertyName("mode")] + public McpSetEnvValueModeDetails Mode { get; set; } +} - /// Current session availability. Disabled availability is reported without making a Connector request. - [JsonPropertyName("availability")] - public ConnectorAvailability Availability { get; set; } +/// Mode controlling how MCP server env values are resolved (`direct` or `indirect`). +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpSetEnvValueModeParams +{ + /// How environment-variable values supplied to MCP servers are resolved. "direct" passes literal string values; "indirect" treats values as references (e.g. names of environment variables on the host) that the runtime resolves before launch. Defaults to the runtime's startup mode; clients that intentionally launch MCP servers with literal values (e.g. CLI prompt mode and ACP) set this to "direct". + [JsonPropertyName("mode")] + public McpSetEnvValueModeDetails Mode { get; set; } - /// Whether connect and reconnect can return an opaque continuation for bounded consent polling. - [JsonPropertyName("consentContinuation")] - public bool ConsentContinuation { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Maximum accepted wall-clock deadline in milliseconds for one continuation call. - [JsonPropertyName("maxDeadlineMs")] - public long MaxDeadlineMs { get; set; } +/// Indicates whether the auto-managed `github` MCP server was removed (false when nothing to remove). +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpRemoveGitHubResult +{ + /// True when the auto-managed `github` MCP server was removed; false when no removal happened (e.g. user has explicitly configured a `github` server, or the server was not registered). + [JsonPropertyName("removed")] + public bool Removed { get; set; } +} - /// Maximum accepted polling attempts for one continuation call. - [JsonPropertyName("maxPollAttempts")] - public long MaxPollAttempts { get; set; } +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionMcpRemoveGitHubRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Maximum accepted delay in milliseconds between polling attempts. - [JsonPropertyName("maxPollIntervalMs")] - public long MaxPollIntervalMs { get; set; } +/// Result of configuring GitHub MCP. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpConfigureGitHubResult +{ + /// Whether GitHub MCP configuration changed. + [JsonPropertyName("changed")] + public bool Changed { get; set; } +} - /// Whether callers select a host-owned GitHub account through an opaque selection ID rather than supplying a provider token. - [JsonPropertyName("opaqueAccountSelection")] - public bool OpaqueAccountSelection { get; set; } +/// Credential-free authentication identity used to configure GitHub MCP. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpConfigureGitHubRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Whether getAccount is supported. Absence means false. - [JsonPropertyName("sessionAccountSelection")] - public bool? SessionAccountSelection { get; set; } +/// Server name and optional configuration for an individual MCP server start. Omit `config` for a config-free start-by-name of an already-configured server. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpStartServerRequest +{ + /// MCP server configuration (stdio process or remote HTTP/SSE). Omit to start the server with its already-registered configuration (config-free start-by-name). + [JsonPropertyName("config")] + public JsonElement? Config { get; set; } - /// Whether reconcile accepts forceConnectorName. Absence means false. - [JsonPropertyName("targetedReconcile")] - public bool? TargetedReconcile { get; set; } + /// Exact receipt identity for explicit owned activation in this session. + [JsonPropertyName("expectedInstallationId")] + public string? ExpectedInstallationId { get; set; } + + /// Name of the MCP server to start. + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } } -/// Identifies the target session. +/// Server name and optional configuration for an individual MCP server start. Omit `config` for a config-free start-by-name of an already-configured server. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionConnectorsGetCapabilitiesRequest +internal sealed class McpStartServerRequestWithSession { + /// MCP server configuration (stdio process or remote HTTP/SSE). Omit to start the server with its already-registered configuration (config-free start-by-name). + [JsonPropertyName("config")] + public JsonElement? Config { get; set; } + + /// Exact receipt identity for explicit owned activation in this session. + [JsonPropertyName("expectedInstallationId")] + public string? ExpectedInstallationId { get; set; } + + /// Name of the MCP server to start. + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Credential-free identity metadata. +/// Server name and optional replacement configuration for an individual MCP server restart. Omit `config` for a config-free restart-by-name of an already-configured server. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AuthIdentityMetadata +public sealed class McpRestartServerRequest { - /// Identity host. - [JsonPropertyName("host")] - public string Host { get; set; } = string.Empty; + /// Replacement MCP server configuration (stdio process or remote HTTP/SSE). Omit to restart the server with its already-registered configuration (config-free restart-by-name). + [JsonPropertyName("config")] + public JsonElement? Config { get; set; } - /// User login. - [JsonPropertyName("login")] - public string Login { get; set; } = string.Empty; + /// Exact receipt identity for an explicit owned restart; configuration overrides are refused. + [JsonPropertyName("expectedInstallationId")] + public string? ExpectedInstallationId { get; set; } - /// Authentication type. - [JsonPropertyName("type")] - public AuthInfoType Type { get; set; } + /// Name of the MCP server to restart. + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } } -/// Session account selection. +/// Server name and optional replacement configuration for an individual MCP server restart. Omit `config` for a config-free restart-by-name of an already-configured server. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorSessionAccount +internal sealed class McpRestartServerRequestWithSession { - /// Opaque session-scoped account selection ID. - [JsonPropertyName("accountId")] - public string AccountId { get; set; } = string.Empty; + /// Replacement MCP server configuration (stdio process or remote HTTP/SSE). Omit to restart the server with its already-registered configuration (config-free restart-by-name). + [JsonPropertyName("config")] + public JsonElement? Config { get; set; } - /// Credential-free identity metadata. - [JsonPropertyName("authInfo")] - public AuthIdentityMetadata AuthInfo { get => field ??= new(); set; } + /// Exact receipt identity for an explicit owned restart; configuration overrides are refused. + [JsonPropertyName("expectedInstallationId")] + public string? ExpectedInstallationId { get; set; } + + /// Name of the MCP server to restart. + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Identifies the target session. +/// Server name for an individual MCP server stop. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionConnectorsGetAccountRequest +public sealed class McpStopServerRequest +{ + /// Exact owned receipt identity. Stop also forgets this session's durable activation. + [JsonPropertyName("expectedInstallationId")] + public string? ExpectedInstallationId { get; set; } + + /// Name of the MCP server to stop. + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } +} + +/// Server name for an individual MCP server stop. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpStopServerRequestWithSession { + /// Exact owned receipt identity. Stop also forgets this session's durable activation. + [JsonPropertyName("expectedInstallationId")] + public string? ExpectedInstallationId { get; set; } + + /// Name of the MCP server to stop. + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Account-targeted authorization update required by the Connector service. The account ID is an opaque host routing identifier; no credential is included. +/// Registration parameters for an external MCP client. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorAuthorizationRequirement +internal sealed class McpRegisterExternalClientRequest { - /// Exact opaque account selection that made the Connector request. - [JsonPropertyName("accountId")] - public string AccountId { get; set; } = string.Empty; + /// Logical server name for the external client. + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; - /// Stable OAuth scope the selected account must grant. - [JsonPropertyName("scope")] - public ConnectorAuthorizationScope Scope { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Credential-free Connector catalog entry. +/// Server name identifying the external client to remove. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorCatalogEntry +internal sealed class McpUnregisterExternalClientRequest { - /// Untrusted service description, when present. - [JsonPropertyName("description")] - public string? Description { get; set; } + /// Server name of the external client to unregister. + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; - /// Untrusted display label from the service. - [JsonPropertyName("displayName")] - public string DisplayName { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Whether the named MCP server is running. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpIsServerRunningResult +{ + /// True if the server has an active client and transport. + [JsonPropertyName("running")] + public bool Running { get; set; } +} + +/// Server name to check running status for. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpIsServerRunningRequest +{ + /// Name of the MCP server to check. + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Indicates whether the pending MCP OAuth response was accepted. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpOauthHandlePendingResult +{ + /// Whether the response was accepted. False if the request was unknown, timed out, or already resolved. + [JsonPropertyName("success")] + public bool Success { get; set; } +} + +/// Host response to the pending OAuth request. +/// Polymorphic base type discriminated by kind. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(McpOauthPendingRequestResponseToken), "token")] +[JsonDerivedType(typeof(McpOauthPendingRequestResponseCancelled), "cancelled")] +public partial class McpOauthPendingRequestResponse +{ + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} + + +/// The token variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class McpOauthPendingRequestResponseToken : McpOauthPendingRequestResponse +{ + /// + [JsonIgnore] + public override string Kind => "token"; + + /// Access token acquired by the SDK host. + [JsonPropertyName("accessToken")] + public required string AccessToken { get; set; } + + /// Token lifetime in seconds, if known. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("expiresIn")] + public long? ExpiresIn { get; set; } + + /// OAuth token type. Defaults to bearer when omitted. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("tokenType")] + public string? TokenType { get; set; } +} + +/// The cancelled variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class McpOauthPendingRequestResponseCancelled : McpOauthPendingRequestResponse +{ + /// + [JsonIgnore] + public override string Kind => "cancelled"; +} + +/// Pending MCP OAuth request ID and host-provided token or cancellation response. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpOauthHandlePendingRequest +{ + /// OAuth request identifier from the mcp.oauth_required event. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; + + /// Host response to the pending OAuth request. + [JsonPropertyName("result")] + public McpOauthPendingRequestResponse Result { get => field ??= new(); set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Identifies the MCP server whose persisted OAuth credentials were updated. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpOauthAuthenticationStateChangedRequest +{ + /// Whether the target session must mint a session-scoped access token instead of reusing a shared access token persisted by another session. + [JsonPropertyName("refreshSessionToken")] + public bool? RefreshSessionToken { get; set; } + + /// Name of the MCP server whose OAuth credentials were updated. Omit only when the host cannot identify the server. + [JsonPropertyName("serverName")] + public string? ServerName { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// An inert runtime-issued login handle. Preparation alone performs no activation or OAuth work. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionMcpOauthPrepareLoginResult +{ + /// Original expiry, not extended by consumption, retries or cancellation. + [JsonPropertyName("expiresAt")] + public DateTimeOffset ExpiresAt { get; set; } + + /// Retain with the original requester and use for one login or cancellation. + [JsonPropertyName("loginId")] + public string LoginId { get; set; } = string.Empty; +} + +/// Effect-free preparation bound to the existing local session, requester and installation, with frozen options. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionMcpOauthPrepareLoginRequest +{ + /// Text shown on the loopback callback page after successful authorisation. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(2048)] + [JsonPropertyName("callbackSuccessMessage")] + public string? CallbackSuccessMessage { get; set; } + + /// Display name used by the incumbent OAuth client-registration flow. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(2048)] + [JsonPropertyName("clientName")] + public string? ClientName { get; set; } + + /// Exact installation identity from owned inventory, never a server-name alias. + [JsonPropertyName("expectedInstallationId")] + public string ExpectedInstallationId { get; set; } = string.Empty; + + /// Request a new authorisation rather than accepting a usable cached grant. + [JsonPropertyName("forceReauth")] + public bool? ForceReauth { get; set; } + + /// Name recorded by the authoritative owned installation receipt. + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// OAuth authorization URL the caller should open, or empty when cached tokens already authenticated the server. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpOauthLoginResult +{ + /// Opaque authorization identifier returned only for a host-managed redirect URI. The runtime also sends it as the OAuth state value, so the callback endpoint can read state and pass it with the full callback URL to session.mcp.oauth.complete. + [JsonPropertyName("authorizationId")] + public string? AuthorizationId { get; set; } + + /// URL the caller should open in a browser to complete OAuth. Omitted when cached tokens were still valid and no browser interaction was needed — the server is already reconnected in that case. For the default loopback flow, the runtime starts its listener before returning. With redirectUri, the host receives the callback and completes it through session.mcp.oauth.complete. The runtime continues the flow in the background and signals completion via session.mcp_server_status_changed. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("authorizationUrl")] + public string? AuthorizationUrl { get; set; } + + /// Runtime-issued owned flow identity; never a server name or installation operation ID. + [JsonPropertyName("loginId")] + public string? LoginId { get; set; } + + /// Explicit outcome for owned sign-in. Manual callers retain their legacy response shape. + [JsonPropertyName("status")] + public McpOwnedOauthLoginStatus? Status { get; set; } +} + +/// Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, callback handling, and static OAuth client selection. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpOauthLoginRequest +{ + /// Optional override for the body text shown on the OAuth loopback callback success page. When omitted, the runtime applies a neutral fallback; callers driving interactive auth should pass surface-specific copy telling the user where to return. + [JsonPropertyName("callbackSuccessMessage")] + public string? CallbackSuccessMessage { get; set; } + + /// Optional OAuth client ID override for this login. When set, the runtime uses this pre-registered static client instead of dynamic client registration. + [JsonPropertyName("clientId")] + public string? ClientId { get; set; } + + /// Optional override for the OAuth client display name shown on the consent screen. Applies to newly registered dynamic clients only — existing registrations keep the name they were created with. When omitted, the runtime applies a neutral fallback; callers driving interactive auth should pass their own surface-specific label so the consent screen matches the product the user sees. + [JsonPropertyName("clientName")] + public string? ClientName { get; set; } + + /// Optional OAuth client secret override for this login. The runtime treats this as an ephemeral host-owned secret, uses it for this authentication attempt and does not persist it. + [JsonPropertyName("clientSecret")] + public string? ClientSecret { get; set; } + + /// Exact owned receipt identity. Owned login never uses an implicit helper session. + [JsonPropertyName("expectedInstallationId")] + public string? ExpectedInstallationId { get; set; } + + /// When true, clears any cached OAuth token for the server and runs a full new authorization. Use when the user explicitly wants to switch accounts or believes their session is stuck. + [JsonPropertyName("forceReauth")] + public bool? ForceReauth { get; set; } + + /// Optional OAuth grant type override for this login. Defaults to the server configuration, or authorization_code when no grant type is specified. + [JsonPropertyName("grantType")] + public McpOauthLoginGrantType? GrantType { get; set; } + + /// + /// Required for owned login. Consumes the exact prepareLogin handle once. + /// Set forceReauth and display options during preparation, not consumption. + /// + [JsonPropertyName("loginId")] + public string? LoginId { get; set; } + + /// Optional override indicating whether the static OAuth client is public. When false, the runtime treats it as confidential and uses the per-login clientSecret if provided, otherwise retrieving the client secret from the MCP OAuth secret store. + [JsonPropertyName("publicClient")] + public bool? PublicClient { get; set; } + + /// Optional externally visible HTTPS redirect URI for a host-managed callback endpoint. When supplied, the runtime still owns discovery, PKCE, token exchange, persistence, and reconnect, but does not bind a loopback listener or terminate HTTPS. The URI must not contain query parameters or a fragment and must be registered for the selected CIMD, DCR, or static OAuth client. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("redirectUri")] + public string? RedirectUri { get; set; } + + /// Name of the remote MCP server to authenticate. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } +} + +/// Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, callback handling, and static OAuth client selection. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpOauthLoginRequestWithSession +{ + /// Optional override for the body text shown on the OAuth loopback callback success page. When omitted, the runtime applies a neutral fallback; callers driving interactive auth should pass surface-specific copy telling the user where to return. + [JsonPropertyName("callbackSuccessMessage")] + public string? CallbackSuccessMessage { get; set; } + + /// Optional OAuth client ID override for this login. When set, the runtime uses this pre-registered static client instead of dynamic client registration. + [JsonPropertyName("clientId")] + public string? ClientId { get; set; } + + /// Optional override for the OAuth client display name shown on the consent screen. Applies to newly registered dynamic clients only — existing registrations keep the name they were created with. When omitted, the runtime applies a neutral fallback; callers driving interactive auth should pass their own surface-specific label so the consent screen matches the product the user sees. + [JsonPropertyName("clientName")] + public string? ClientName { get; set; } + + /// Optional OAuth client secret override for this login. The runtime treats this as an ephemeral host-owned secret, uses it for this authentication attempt and does not persist it. + [JsonPropertyName("clientSecret")] + public string? ClientSecret { get; set; } + + /// Exact owned receipt identity. Owned login never uses an implicit helper session. + [JsonPropertyName("expectedInstallationId")] + public string? ExpectedInstallationId { get; set; } + + /// When true, clears any cached OAuth token for the server and runs a full new authorization. Use when the user explicitly wants to switch accounts or believes their session is stuck. + [JsonPropertyName("forceReauth")] + public bool? ForceReauth { get; set; } + + /// Optional OAuth grant type override for this login. Defaults to the server configuration, or authorization_code when no grant type is specified. + [JsonPropertyName("grantType")] + public McpOauthLoginGrantType? GrantType { get; set; } + + /// + /// Required for owned login. Consumes the exact prepareLogin handle once. + /// Set forceReauth and display options during preparation, not consumption. + /// + [JsonPropertyName("loginId")] + public string? LoginId { get; set; } + + /// Optional override indicating whether the static OAuth client is public. When false, the runtime treats it as confidential and uses the per-login clientSecret if provided, otherwise retrieving the client secret from the MCP OAuth secret store. + [JsonPropertyName("publicClient")] + public bool? PublicClient { get; set; } + + /// Optional externally visible HTTPS redirect URI for a host-managed callback endpoint. When supplied, the runtime still owns discovery, PKCE, token exchange, persistence, and reconnect, but does not bind a loopback listener or terminate HTTPS. The URI must not contain query parameters or a fragment and must be registered for the selected CIMD, DCR, or static OAuth client. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("redirectUri")] + public string? RedirectUri { get; set; } + + /// Name of the remote MCP server to authenticate. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Host-delivered callback for a runtime-managed MCP OAuth login. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpOauthCompleteRequest +{ + /// Opaque identifier returned by session.mcp.oauth.login for the pending external callback. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("authorizationId")] + public string AuthorizationId { get; set; } = string.Empty; + + /// Full externally visible HTTPS callback URL received by the host, including the authorization response query parameters. Applications behind a reverse proxy must reconstruct the public URL rather than passing an internal proxy URL. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("callbackUrl")] + public string CallbackUrl { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Passive MCP OAuth probe result. `authenticated` means the server accepted the probe request while an OAuth-origin access token was attached; it does not prove the server required or independently validated that token. The probe does not make a second unauthenticated request. Failed is an expected probe-domain outcome; JSON-RPC errors are reserved for API-call failures. +/// Polymorphic base type discriminated by status. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "status", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(McpOauthProbeResultNoAuthRequired), "no-auth-required")] +[JsonDerivedType(typeof(McpOauthProbeResultAuthenticated), "authenticated")] +[JsonDerivedType(typeof(McpOauthProbeResultNeedsAuth), "needs-auth")] +[JsonDerivedType(typeof(McpOauthProbeResultFailed), "failed")] +public partial class McpOauthProbeResult +{ + /// The type discriminator. + [JsonPropertyName("status")] + public virtual string Status { get; set; } = string.Empty; +} + + +/// The no-auth-required variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class McpOauthProbeResultNoAuthRequired : McpOauthProbeResult +{ + /// + [JsonIgnore] + public override string Status => "no-auth-required"; + + /// HTTP response returned by the server. + [JsonPropertyName("httpResponse")] + public required McpOauthHttpResponse HttpResponse { get; set; } +} + +/// The authenticated variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class McpOauthProbeResultAuthenticated : McpOauthProbeResult +{ + /// + [JsonIgnore] + public override string Status => "authenticated"; + + /// HTTP response returned by the server. + [JsonPropertyName("httpResponse")] + public required McpOauthHttpResponse HttpResponse { get; set; } +} + +/// The needs-auth variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class McpOauthProbeResultNeedsAuth : McpOauthProbeResult +{ + /// + [JsonIgnore] + public override string Status => "needs-auth"; + + /// HTTP 401 or 403 response returned by the server. + [JsonPropertyName("httpResponse")] + public required McpOauthHttpResponse HttpResponse { get; set; } + + /// Why authentication is needed. + [JsonPropertyName("reason")] + public required McpOauthProbeNeedsAuthReason Reason { get; set; } + + /// Parsed WWW-Authenticate challenge parameters, when present and parseable. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("wwwAuthenticateParams")] + public McpOauthWWWAuthenticateParams? WwwAuthenticateParams { get; set; } +} + +/// The failed variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class McpOauthProbeResultFailed : McpOauthProbeResult +{ + /// + [JsonIgnore] + public override string Status => "failed"; + + /// Human-readable probe failure detail. + [JsonPropertyName("error")] + public required string Error { get; set; } + + /// HTTP response returned by the server, when the probe reached the server and captured the complete response. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("httpResponse")] + public McpOauthHttpResponse? HttpResponse { get; set; } +} + +/// Remote MCP server name for a passive OAuth status probe. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpOauthProbeRequest +{ + /// Exact owned receipt identity; probing never activates a dormant installation. + [JsonPropertyName("expectedInstallationId")] + public string? ExpectedInstallationId { get; set; } + + /// Name of the configured remote MCP server to probe. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } +} + +/// Remote MCP server name for a passive OAuth status probe. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpOauthProbeRequestWithSession +{ + /// Exact owned receipt identity; probing never activates a dormant installation. + [JsonPropertyName("expectedInstallationId")] + public string? ExpectedInstallationId { get; set; } + + /// Name of the configured remote MCP server to probe. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Honest terminal cancellation result; persistence or recovery failures remain RPC errors. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionMcpOauthCancelLoginResult +{ + /// True after cancellation settles, false when the original login already connected successfully. + [JsonPropertyName("cancelled")] + public bool Cancelled { get; set; } +} + +/// Targets only the original prepared/applying owned login on this exact session requester. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionMcpOauthCancelLoginRequest +{ + /// The same authoritative installation identity supplied during preparation. + [JsonPropertyName("expectedInstallationId")] + public string ExpectedInstallationId { get; set; } = string.Empty; + + /// Runtime-issued login handle known before the effectful login request begins. + [JsonPropertyName("loginId")] + public string LoginId { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Indicates whether the pending MCP OAuth response was accepted. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpOauthRespondResult +{ + /// Whether the response was accepted. False if the request was unknown, timed out, or already resolved. + [JsonPropertyName("success")] + public bool Success { get; set; } +} + +/// Pending MCP OAuth request id to respond to. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpOauthRespondRequest +{ + /// OAuth request identifier from the mcp.oauth_required event. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Indicates whether the pending MCP headers refresh response was accepted. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpHeadersHandlePendingHeadersRefreshRequestResult +{ + /// Whether the response was accepted. False if the request was unknown, timed out, or already resolved. + [JsonPropertyName("success")] + public bool Success { get; set; } +} + +/// Host response: supply dynamic headers or decline this refresh. +/// Polymorphic base type discriminated by kind. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(McpHeadersHandlePendingHeadersRefreshRequestHeaders), "headers")] +[JsonDerivedType(typeof(McpHeadersHandlePendingHeadersRefreshRequestNone), "none")] +[JsonDerivedType(typeof(McpHeadersHandlePendingHeadersRefreshRequestError), "error")] +public partial class McpHeadersHandlePendingHeadersRefreshRequest +{ + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} + + +/// The headers variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class McpHeadersHandlePendingHeadersRefreshRequestHeaders : McpHeadersHandlePendingHeadersRefreshRequest +{ + /// + [JsonIgnore] + public override string Kind => "headers"; + + /// Headers to overlay onto the MCP request. Dynamic headers override static config headers but do not replace SDK-managed request headers. + [JsonPropertyName("headers")] + public required IDictionary Headers { get; set; } + + /// Optional lifetime in milliseconds for these returned headers. The runtime clamps its configured cache lifetime to this value. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("ttlMs")] + public long? TtlMs { get; set; } +} + +/// The none variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class McpHeadersHandlePendingHeadersRefreshRequestNone : McpHeadersHandlePendingHeadersRefreshRequest +{ + /// + [JsonIgnore] + public override string Kind => "none"; +} + +/// The error variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class McpHeadersHandlePendingHeadersRefreshRequestError : McpHeadersHandlePendingHeadersRefreshRequest +{ + /// + [JsonIgnore] + public override string Kind => "error"; + + /// Host credential broker failure, denial, or revocation reason. + [JsonPropertyName("message")] + public required string Message { get; set; } +} + +/// MCP headers refresh request id and the host response. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpHeadersHandlePendingHeadersRefreshRequestRequest +{ + /// Headers refresh request identifier from mcp.headers_refresh_required. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; + + /// Host response: supply dynamic headers or decline this refresh. + [JsonPropertyName("result")] + public McpHeadersHandlePendingHeadersRefreshRequest Result { get => field ??= new(); set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// MCP Apps resource content with URI, optional MIME type, text or base64 blob, and resource metadata. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpAppsResourceContent +{ + /// Resource-level metadata (CSP, permissions, etc.). + [JsonPropertyName("_meta")] + public IDictionary? Meta { get; set; } + + /// Base64-encoded binary content. + [JsonPropertyName("blob")] + public string? Blob { get; set; } + + /// MIME type of the content. + [JsonPropertyName("mimeType")] + public string? MimeType { get; set; } + + /// Text content (e.g. HTML). + [JsonPropertyName("text")] + public string? Text { get; set; } + + /// The resource URI (typically ui://...). + [JsonPropertyName("uri")] + public string Uri { get; set; } = string.Empty; +} + +/// Resource contents returned by the MCP server. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpAppsReadResourceResult +{ + /// Resource contents returned by the server. + [JsonPropertyName("contents")] + public IList Contents { get => field ??= []; set; } +} + +/// MCP server and resource URI to fetch. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpAppsReadResourceRequest +{ + /// Name of the MCP server hosting the resource. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; + + /// Resource URI (typically ui://...). + [JsonPropertyName("uri")] + public string Uri { get; set; } = string.Empty; +} + +/// App-callable tools from the named MCP server. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpAppsListToolsResult +{ + /// App-callable tools from the server. + [JsonPropertyName("tools")] + public IList> Tools { get => field ??= []; set; } +} + +/// MCP server to list app-callable tools for. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpAppsListToolsRequest +{ + /// **Required.** Server whose ui:// view issued the request. Per SEP-1865 ('callable by the app from this server only'), the call is rejected when this differs from `serverName`, and rejected outright when missing. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("originServerName")] + public string OriginServerName { get; set; } = string.Empty; + + /// MCP server hosting the app. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// MCP server, tool name, and arguments to invoke from an MCP App view. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpAppsCallToolRequest +{ + /// Tool arguments. + [JsonPropertyName("arguments")] + public IDictionary? Arguments { get; set; } + + /// **Required.** Server whose ui:// view issued the request. Per SEP-1865 ('callable by the app from this server only'), the call is rejected when this differs from `serverName`, and rejected outright when missing. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("originServerName")] + public string OriginServerName { get; set; } = string.Empty; + + /// MCP server hosting the tool. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; + + /// MCP tool name. + [JsonPropertyName("toolName")] + public string ToolName { get; set; } = string.Empty; +} + +/// Host context advertised to MCP App guests. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpAppsSetHostContextDetails +{ + /// Display modes the host supports. + [JsonPropertyName("availableDisplayModes")] + public IList? AvailableDisplayModes { get; set; } + + /// Current display mode (SEP-1865). + [JsonPropertyName("displayMode")] + public McpAppsSetHostContextDetailsDisplayMode? DisplayMode { get; set; } + + /// BCP-47 locale, e.g. 'en-US'. + [JsonPropertyName("locale")] + public string? Locale { get; set; } + + /// Platform type for responsive design. + [JsonPropertyName("platform")] + public McpAppsSetHostContextDetailsPlatform? Platform { get; set; } + + /// UI theme preference per SEP-1865. + [JsonPropertyName("theme")] + public McpAppsSetHostContextDetailsTheme? Theme { get; set; } + + /// IANA timezone, e.g. 'America/New_York'. + [JsonPropertyName("timeZone")] + public string? TimeZone { get; set; } + + /// Host application identifier. + [JsonPropertyName("userAgent")] + public string? UserAgent { get; set; } +} + +/// Host context to advertise to MCP App guests. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpAppsSetHostContextRequest +{ + /// Host context advertised to MCP App guests. + [JsonPropertyName("context")] + public McpAppsSetHostContextDetails Context { get => field ??= new(); set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Current host context. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpAppsHostContextDetails +{ + /// Display modes the host supports. + [JsonPropertyName("availableDisplayModes")] + public IList? AvailableDisplayModes { get; set; } + + /// Current display mode (SEP-1865). + [JsonPropertyName("displayMode")] + public McpAppsHostContextDetailsDisplayMode? DisplayMode { get; set; } + + /// BCP-47 locale, e.g. 'en-US'. + [JsonPropertyName("locale")] + public string? Locale { get; set; } + + /// Platform type for responsive design. + [JsonPropertyName("platform")] + public McpAppsHostContextDetailsPlatform? Platform { get; set; } + + /// UI theme preference per SEP-1865. + [JsonPropertyName("theme")] + public McpAppsHostContextDetailsTheme? Theme { get; set; } + + /// IANA timezone, e.g. 'America/New_York'. + [JsonPropertyName("timeZone")] + public string? TimeZone { get; set; } + + /// Host application identifier. + [JsonPropertyName("userAgent")] + public string? UserAgent { get; set; } +} + +/// Current host context advertised to MCP App guests. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpAppsHostContext +{ + /// Current host context. + [JsonPropertyName("context")] + public McpAppsHostContextDetails Context { get => field ??= new(); set; } +} + +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionMcpAppsGetHostContextRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Capability negotiation snapshot. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpAppsDiagnoseCapability +{ + /// Whether the runtime advertises `extensions.io.modelcontextprotocol/ui` to MCP servers. + [JsonPropertyName("advertised")] + public bool Advertised { get; set; } + + /// Whether the MCP_APPS feature flag (or COPILOT_MCP_APPS env override) is on. + [JsonPropertyName("featureFlagEnabled")] + public bool FeatureFlagEnabled { get; set; } + + /// Whether the session has the `mcp-apps` capability. + [JsonPropertyName("sessionHasMcpApps")] + public bool SessionHasMcpApps { get; set; } +} + +/// What the server returned for this session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpAppsDiagnoseServer +{ + /// Whether the named server is currently connected. + [JsonPropertyName("connected")] + public bool Connected { get; set; } + + /// Up to 5 tool names with `_meta.ui` for quick inspection. + [JsonPropertyName("sampleToolNames")] + public IList SampleToolNames { get => field ??= []; set; } + + /// Total tools returned by the server's tools/list. + [JsonPropertyName("toolCount")] + public double ToolCount { get; set; } + + /// Tools whose `_meta.ui` is populated (resourceUri and/or visibility set). + [JsonPropertyName("toolsWithUiMeta")] + public double ToolsWithUiMeta { get; set; } +} + +/// Diagnostic snapshot of MCP Apps wiring for the named server. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpAppsDiagnoseResult +{ + /// Capability negotiation snapshot. + [JsonPropertyName("capability")] + public McpAppsDiagnoseCapability Capability { get => field ??= new(); set; } + + /// What the server returned for this session. + [JsonPropertyName("server")] + public McpAppsDiagnoseServer Server { get => field ??= new(); set; } +} + +/// MCP server to diagnose MCP Apps wiring for. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpAppsDiagnoseRequest +{ + /// MCP server to probe. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// MCP resource content with URI, optional MIME type, text or base64 blob, and resource metadata. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpResourceContent +{ + /// Resource-level metadata (CSP, permissions, etc.). + [JsonPropertyName("_meta")] + public IDictionary? Meta { get; set; } + + /// Base64-encoded binary content. + [JsonPropertyName("blob")] + public string? Blob { get; set; } + + /// MIME type of the content. + [JsonPropertyName("mimeType")] + public string? MimeType { get; set; } + + /// Text content (e.g. HTML). + [JsonPropertyName("text")] + public string? Text { get; set; } + + /// The resource URI. + [JsonPropertyName("uri")] + public string Uri { get; set; } = string.Empty; +} + +/// Resource contents returned by the MCP server. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpResourcesReadResult +{ + /// Resource contents returned by the server. + [JsonPropertyName("contents")] + public IList Contents { get => field ??= []; set; } +} + +/// MCP server and resource URI to fetch. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpResourcesReadRequest +{ + /// Name of the MCP server hosting the resource. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; + + /// Resource URI. + [JsonPropertyName("uri")] + public string Uri { get; set; } = string.Empty; +} + +/// Standard MCP resource annotations plus preserved non-standard annotation fields. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpResourceAnnotations +{ + /// Server-provided non-standard annotation fields preserved from the MCP response. + [JsonPropertyName("additionalProperties")] + public IDictionary? AdditionalProperties { get; set; } + + /// Intended audience roles for this resource. + [JsonPropertyName("audience")] + public IList? Audience { get; set; } + + /// Last-modified timestamp hint. + [JsonPropertyName("lastModified")] + public string? LastModified { get; set; } + + /// Priority hint for model/client use. + [JsonPropertyName("priority")] + public double? Priority { get; set; } +} + +/// A resource icon descriptor plus preserved non-standard icon fields. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpResourceIcon +{ + /// Server-provided non-standard icon fields preserved from the MCP response. + [JsonPropertyName("additionalProperties")] + public IDictionary? AdditionalProperties { get; set; } + + /// Icon MIME type, when known. + [JsonPropertyName("mimeType")] + public string? MimeType { get; set; } + + /// Icon sizes hint. + [JsonPropertyName("sizes")] + public string? Sizes { get; set; } + + /// Icon URI. + [JsonPropertyName("src")] + public string Src { get; set; } = string.Empty; + + /// Theme hint for this icon. + [JsonPropertyName("theme")] + public string? Theme { get; set; } +} + +/// An MCP resource descriptor (spec `Resource`): URI, name, and optional title, description, MIME type, size, icons, annotations, and metadata. Server-provided fields outside the standard descriptor shape are exposed under `additionalProperties`. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpResource +{ + /// Resource-level metadata. + [JsonPropertyName("_meta")] + public IDictionary? Meta { get; set; } + + /// Server-provided non-standard descriptor fields preserved from the MCP response. + [JsonPropertyName("additionalProperties")] + public IDictionary? AdditionalProperties { get; set; } + + /// Model/client annotations associated with this resource. + [JsonPropertyName("annotations")] + public McpResourceAnnotations? Annotations { get; set; } + + /// Optional description of what this resource represents. + [JsonPropertyName("description")] + public string? Description { get; set; } + + /// Icons associated with this resource. + [JsonPropertyName("icons")] + public IList? Icons { get; set; } + + /// MIME type of the resource, if known. + [JsonPropertyName("mimeType")] + public string? MimeType { get; set; } + + /// The programmatic name of the resource. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Resource size in bytes, when known. + [JsonPropertyName("size")] + public long? Size { get; set; } + + /// Optional human-readable display title. + [JsonPropertyName("title")] + public string? Title { get; set; } + + /// The resource URI (e.g. ui://... or file:///...). + [JsonPropertyName("uri")] + public string Uri { get; set; } = string.Empty; +} + +/// One page of resources advertised by the named MCP server. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpResourcesListResult +{ + /// Opaque cursor for the next page, if the server has more resources. + [JsonPropertyName("nextCursor")] + public string? NextCursor { get; set; } + + /// Resources advertised by the server (proxied MCP `resources/list`). + [JsonPropertyName("resources")] + public IList Resources { get => field ??= []; set; } +} + +/// MCP server whose resources to enumerate. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpResourcesListRequest +{ + /// Opaque MCP pagination cursor from a prior `nextCursor` value. + [JsonPropertyName("cursor")] + public string? Cursor { get; set; } + + /// Name of the MCP server whose resources to enumerate. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// An MCP resource template descriptor (spec `ResourceTemplate`): an RFC 6570 URI template, name, and optional title, description, MIME type, icons, annotations, and metadata. Server-provided fields outside the standard descriptor shape are exposed under `additionalProperties`. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpResourceTemplate +{ + /// Resource-template-level metadata. + [JsonPropertyName("_meta")] + public IDictionary? Meta { get; set; } + + /// Server-provided non-standard descriptor fields preserved from the MCP response. + [JsonPropertyName("additionalProperties")] + public IDictionary? AdditionalProperties { get; set; } + + /// Model/client annotations associated with this template. + [JsonPropertyName("annotations")] + public McpResourceAnnotations? Annotations { get; set; } + + /// Optional description of what this template is for. + [JsonPropertyName("description")] + public string? Description { get; set; } + + /// Icons associated with resources matching this template. + [JsonPropertyName("icons")] + public IList? Icons { get; set; } + + /// MIME type for resources matching this template, if uniform. + [JsonPropertyName("mimeType")] + public string? MimeType { get; set; } + + /// The programmatic name of the resource template. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Optional human-readable display title. + [JsonPropertyName("title")] + public string? Title { get; set; } + + /// An RFC 6570 URI template for constructing resource URIs. + [JsonPropertyName("uriTemplate")] + public string UriTemplate { get; set; } = string.Empty; +} + +/// One page of resource templates advertised by the named MCP server. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpResourcesListTemplatesResult +{ + /// Opaque cursor for the next page, if the server has more resource templates. + [JsonPropertyName("nextCursor")] + public string? NextCursor { get; set; } + + /// Resource templates advertised by the server (proxied MCP `resources/templates/list`). + [JsonPropertyName("resourceTemplates")] + public IList ResourceTemplates { get => field ??= []; set; } +} + +/// MCP server whose resource templates to enumerate. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpResourcesListTemplatesRequest +{ + /// Opaque MCP pagination cursor from a prior `nextCursor` value. + [JsonPropertyName("cursor")] + public string? Cursor { get; set; } + + /// Name of the MCP server whose resource templates to enumerate. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// An argument accepted by an MCP prompt. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpPromptArgument +{ + /// Argument-level metadata. + [JsonPropertyName("_meta")] + public IDictionary? Meta { get; set; } + + /// Server-provided non-standard argument fields. + [JsonPropertyName("additionalProperties")] + public IDictionary? AdditionalProperties { get; set; } + + /// Description of the argument. + [JsonPropertyName("description")] + public string? Description { get; set; } + + /// Name of the argument. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Whether the argument is required; omission is distinct from false. + [JsonPropertyName("required")] + public bool? Required { get; set; } +} + +/// An MCP prompt icon with standard size hints and preserved non-standard fields. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpPromptIcon +{ + /// Server-provided non-standard icon fields. + [JsonPropertyName("additionalProperties")] + public IDictionary? AdditionalProperties { get; set; } + + /// Icon MIME type, when known. + [JsonPropertyName("mimeType")] + public string? MimeType { get; set; } + + /// Icon sizes, such as `48x48` or `any`. + [JsonPropertyName("sizes")] + public IList? Sizes { get; set; } + + /// Icon URI. + [JsonPropertyName("src")] + public string Src { get; set; } = string.Empty; + + /// Theme hint for this icon. + [JsonPropertyName("theme")] + public string? Theme { get; set; } +} + +/// An MCP prompt descriptor. Server-provided non-standard fields are exposed under `additionalProperties`. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpPrompt +{ + /// Prompt-level metadata. + [JsonPropertyName("_meta")] + public IDictionary? Meta { get; set; } + + /// Server-provided non-standard descriptor fields. + [JsonPropertyName("additionalProperties")] + public IDictionary? AdditionalProperties { get; set; } + + /// Arguments accepted by the prompt. + [JsonPropertyName("arguments")] + public IList? Arguments { get; set; } + + /// Description of what this prompt provides. + [JsonPropertyName("description")] + public string? Description { get; set; } + + /// Icons associated with this prompt. + [JsonPropertyName("icons")] + public IList? Icons { get; set; } + + /// The programmatic name of the prompt. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Human-readable display title. + [JsonPropertyName("title")] + public string? Title { get; set; } +} + +/// One page of prompts advertised by the named MCP server. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpPromptsListResult +{ + /// MCP result metadata. + [JsonPropertyName("_meta")] + public IDictionary? Meta { get; set; } + + /// Server-provided non-standard result fields. + [JsonPropertyName("additionalProperties")] + public IDictionary? AdditionalProperties { get; set; } + + /// Opaque cursor for the next page, if the server has more prompts. + [JsonPropertyName("nextCursor")] + public string? NextCursor { get; set; } + + /// Prompts advertised by the server. + [JsonPropertyName("prompts")] + public IList Prompts { get => field ??= []; set; } +} + +/// MCP server whose prompts to enumerate. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpPromptsListRequest +{ + /// Opaque MCP pagination cursor from a prior `nextCursor` value. + [JsonPropertyName("cursor")] + public string? Cursor { get; set; } + + /// Name of the MCP server whose prompts to enumerate. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// An MCP prompt message with opaque JSON content preserved without flattening or content-type filtering. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpPromptMessage +{ + /// Message-level metadata. + [JsonPropertyName("_meta")] + public IDictionary? Meta { get; set; } + + /// Server-provided non-standard message fields. + [JsonPropertyName("additionalProperties")] + public IDictionary? AdditionalProperties { get; set; } + + /// The original MCP content block, including nested metadata and unfamiliar content types. + [JsonPropertyName("content")] + public JsonElement Content { get; set; } + + /// The role of the message sender. + [JsonPropertyName("role")] + public McpPromptRole Role { get; set; } +} + +/// Prompt messages returned by the MCP server without sending them to the model. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpPromptsGetResult +{ + /// MCP result metadata. + [JsonPropertyName("_meta")] + public IDictionary? Meta { get; set; } + + /// Server-provided non-standard result fields. + [JsonPropertyName("additionalProperties")] + public IDictionary? AdditionalProperties { get; set; } + + /// Description of the prompt. + [JsonPropertyName("description")] + public string? Description { get; set; } + + /// Ordered prompt messages. + [JsonPropertyName("messages")] + public IList Messages { get => field ??= []; set; } +} + +/// MCP server, prompt name, and optional string-valued arguments. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpPromptsGetRequest +{ + /// String-valued arguments to pass to the prompt. + [JsonPropertyName("arguments")] + public IDictionary? Arguments { get; set; } + + /// The programmatic name of the prompt. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("promptName")] + public string PromptName { get; set; } = string.Empty; + + /// Name of the MCP server hosting the prompt. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// MCP diagnostic source configuration. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpDiagnosticSourceConfiguration +{ + /// Threshold to apply to MCP diagnostic producers in this session. + [JsonPropertyName("level")] + public DiagnosticLogLevel Level { get; set; } +} + +/// Typed diagnostic source configuration. At least one source is required by diagnostics configuration methods. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class DiagnosticSourcesConfiguration +{ + /// MCP diagnostic capture threshold. Omit to leave the current threshold unchanged. + [JsonPropertyName("mcp")] + public McpDiagnosticSourceConfiguration? Mcp { get; set; } +} + +/// Per-source session diagnostics configuration. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class DiagnosticsConfiguration +{ + /// Diagnostic thresholds keyed by supported source. + [JsonPropertyName("sources")] + public DiagnosticSourcesConfiguration Sources { get => field ??= new(); set; } +} + +/// Patch session diagnostic thresholds for explicitly supplied sources. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class DiagnosticsConfigureRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; + + /// Sources to configure. At least one supported source must be supplied. + [JsonPropertyName("sources")] + public DiagnosticSourcesConfiguration Sources { get => field ??= new(); set; } +} + +/// One retained session-scoped diagnostic record. Potentially content-bearing diagnostic data is opt-in and must not be exported automatically as telemetry. +/// Polymorphic base type discriminated by source. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "source", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(DiagnosticEntryMcp), "mcp")] +public partial class DiagnosticEntry +{ + /// The type discriminator. + [JsonPropertyName("source")] + public virtual string Source { get; set; } = string.Empty; +} + + +/// MCP-specific detail for a source-discriminated diagnostic entry. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpDiagnosticDetails +{ + /// Fresh identifier for the MCP connection attempt, including failed starts. + [JsonPropertyName("connectionId")] + public string ConnectionId { get; set; } = string.Empty; + + /// Serialized diagnostic detail. Protocol and HTTP records use JSON when detail is present. + [JsonPropertyName("data")] + public string? Data { get; set; } + + /// Protocol-frame direction when kind is protocol. + [JsonPropertyName("direction")] + public McpDiagnosticDirection? Direction { get; set; } + + /// Diagnostic record category. + [JsonPropertyName("kind")] + public McpDiagnosticKind Kind { get; set; } + + /// Configured MCP server name. + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; +} + +/// The mcp variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class DiagnosticEntryMcp : DiagnosticEntry +{ + /// + [JsonIgnore] + public override string Source => "mcp"; + + /// Agent identifier for a subagent host. Omitted for the root agent. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("agentId")] + public string? AgentId { get; set; } + + /// Typed MCP diagnostic detail. + [JsonPropertyName("details")] + public required McpDiagnosticDetails Details { get; set; } + + /// Severity of this emitted diagnostic record. + [JsonPropertyName("level")] + public required DiagnosticSeverity Level { get; set; } + + /// Human-readable diagnostic summary. + [JsonPropertyName("message")] + public required string Message { get; set; } + + /// Original byte count when a known-size message or data value was truncated. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("originalBytes")] + public long? OriginalBytes { get; set; } + + /// UTC RFC 3339 timestamp captured at the diagnostic source. + [JsonPropertyName("timestamp")] + public required string Timestamp { get; set; } + + /// Whether message or data was truncated to the record-size bound. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("truncated")] + public bool? Truncated { get; set; } +} + +/// One cursor-addressed page of retained session diagnostics. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class DiagnosticsReadResult +{ + /// Opaque cursor for the next independent read. + [JsonPropertyName("cursor")] + public string Cursor { get; set; } = string.Empty; + + /// Whether the requested cursor remained within the retained buffer window. + [JsonPropertyName("cursorStatus")] + public DiagnosticCursorStatus CursorStatus { get; set; } + + /// Number of records lost before this page when known. Omitted when a buffer generation change makes the count unknowable. + [JsonPropertyName("droppedCount")] + public long? DroppedCount { get; set; } + + /// Retained records beginning at the requested cursor. + [JsonPropertyName("entries")] + public IList Entries { get => field ??= []; set; } + + /// Whether additional retained records follow this page. + [JsonPropertyName("hasMore")] + public bool HasMore { get; set; } +} + +/// Cursor-based request for session diagnostics. The default limit is 100 (maximum 500); the default waitMs is zero (maximum 30000). +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class DiagnosticsReadRequest +{ + /// Opaque cursor returned by an earlier read. Omit to start at the oldest retained record. + [JsonPropertyName("cursor")] + public string? Cursor { get; set; } + + /// Maximum number of records to return, from 1 through 500. Omit for 100. + [JsonPropertyName("max")] + public long? Max { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; + + /// Nonempty selection of sources to read. Each source may be listed once. + [JsonPropertyName("sources")] + public IList Sources { get => field ??= []; set; } + + /// Maximum time in milliseconds to wait for a new record, from 0 through 30000. + [JsonPropertyName("waitMs")] + public int? WaitMs { get; set; } +} + +/// Feature detection and hard polling limits for the EXPERIMENTAL session connector API. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ConnectorCapabilities +{ + /// Connector API contract version. + [JsonPropertyName("apiVersion")] + public long ApiVersion { get; set; } + + /// Current session availability. Disabled availability is reported without making a Connector request. + [JsonPropertyName("availability")] + public ConnectorAvailability Availability { get; set; } + + /// Whether connect and reconnect can return an opaque continuation for bounded consent polling. + [JsonPropertyName("consentContinuation")] + public bool ConsentContinuation { get; set; } + + /// Maximum accepted wall-clock deadline in milliseconds for one continuation call. + [JsonPropertyName("maxDeadlineMs")] + public long MaxDeadlineMs { get; set; } + + /// Maximum accepted polling attempts for one continuation call. + [JsonPropertyName("maxPollAttempts")] + public long MaxPollAttempts { get; set; } + + /// Maximum accepted delay in milliseconds between polling attempts. + [JsonPropertyName("maxPollIntervalMs")] + public long MaxPollIntervalMs { get; set; } + + /// Whether callers select a host-owned GitHub account through an opaque selection ID rather than supplying a provider token. + [JsonPropertyName("opaqueAccountSelection")] + public bool OpaqueAccountSelection { get; set; } + + /// Whether getAccount is supported. Absence means false. + [JsonPropertyName("sessionAccountSelection")] + public bool? SessionAccountSelection { get; set; } + + /// Whether reconcile accepts forceConnectorName. Absence means false. + [JsonPropertyName("targetedReconcile")] + public bool? TargetedReconcile { get; set; } +} + +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionConnectorsGetCapabilitiesRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Credential-free identity metadata. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class AuthIdentityMetadata +{ + /// Identity host. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; + + /// User login. + [JsonPropertyName("login")] + public string Login { get; set; } = string.Empty; + + /// Authentication type. + [JsonPropertyName("type")] + public AuthInfoType Type { get; set; } +} + +/// Session account selection. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ConnectorSessionAccount +{ + /// Opaque session-scoped account selection ID. + [JsonPropertyName("accountId")] + public string AccountId { get; set; } = string.Empty; + + /// Credential-free identity metadata. + [JsonPropertyName("authInfo")] + public AuthIdentityMetadata AuthInfo { get => field ??= new(); set; } +} + +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionConnectorsGetAccountRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Account-targeted authorization update required by the Connector service. The account ID is an opaque host routing identifier; no credential is included. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ConnectorAuthorizationRequirement +{ + /// Exact opaque account selection that made the Connector request. + [JsonPropertyName("accountId")] + public string AccountId { get; set; } = string.Empty; + + /// Stable OAuth scope the selected account must grant. + [JsonPropertyName("scope")] + public ConnectorAuthorizationScope Scope { get; set; } +} + +/// Credential-free Connector catalog entry. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ConnectorCatalogEntry +{ + /// Untrusted service description, when present. + [JsonPropertyName("description")] + public string? Description { get; set; } + + /// Untrusted display label from the service. + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } = string.Empty; + + /// Optional catalog logo. + [JsonPropertyName("logo")] + public string? Logo { get; set; } + + /// Canonical Connector name used by lifecycle methods. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Optional catalog release tag. + [JsonPropertyName("releaseTag")] + public string? ReleaseTag { get; set; } + + /// Opaque stable runtime IDs currently projected into the session for this Connector. + [JsonPropertyName("runtimeServerIds")] + public IList RuntimeServerIds { get => field ??= []; set; } + + /// Current authoritative service connection state. + [JsonPropertyName("status")] + public ConnectorCatalogStatus Status { get; set; } + + /// Optional catalog tier. + [JsonPropertyName("tier")] + public string? Tier { get; set; } +} + +/// Validated Connector catalog snapshot cached by the session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ConnectorCatalogResult +{ + /// Validated catalog entries in service order. + [JsonPropertyName("connectors")] + public IList Connectors { get => field ??= []; set; } + + /// Unix epoch milliseconds when this snapshot was accepted. + [JsonPropertyName("refreshedAtMs")] + public long RefreshedAtMs { get; set; } + + /// Monotonically increasing session-local catalog revision. + [JsonPropertyName("revision")] + public long Revision { get; set; } +} + +/// Live status of one session-owned MCP projection. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ConnectorRuntimeStatus +{ + /// Canonical Connector name that owns this server. + [JsonPropertyName("connectorName")] + public string ConnectorName { get; set; } = string.Empty; + + /// Opaque runtime server ID. + [JsonPropertyName("runtimeServerId")] + public string RuntimeServerId { get; set; } = string.Empty; + + /// Current live MCP host status. + [JsonPropertyName("status")] + public ConnectorMcpStatus Status { get; set; } +} + +/// Authoritative session connector state. Account IDs are opaque routing identifiers and credentials are never included. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ConnectorStatus +{ + /// Opaque account selection pinned to this session, when one has been selected. + [JsonPropertyName("accountId")] + public string? AccountId { get; set; } + + /// Connector API contract version. + [JsonPropertyName("apiVersion")] + public long ApiVersion { get; set; } + + /// Exact selected account and stable scope requiring an authorization update, when proven by the Connector service. + [JsonPropertyName("authorizationRequirement")] + public ConnectorAuthorizationRequirement? AuthorizationRequirement { get; set; } + + /// Current feature and session availability. + [JsonPropertyName("availability")] + public ConnectorAvailability Availability { get; set; } + + /// Latest validated catalog snapshot, when available. + [JsonPropertyName("catalog")] + public ConnectorCatalogResult? Catalog { get; set; } + + /// Number of active opaque connection continuations. + [JsonPropertyName("pendingConnections")] + public long PendingConnections { get; set; } + + /// Live MCP status for every Connector-owned runtime server. + [JsonPropertyName("runtimeServers")] + public IList RuntimeServers { get => field ??= []; set; } +} + +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionConnectorsGetStatusRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Pins a Connector operation to one host-owned GitHub account through its opaque selection ID. Provider tokens are never accepted. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ConnectorAccountRequest +{ + /// Opaque account selection ID previously returned by an account discovery API. + [JsonPropertyName("accountId")] + public string AccountId { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Typed result of initiating or continuing a Connector connection. +/// Polymorphic base type discriminated by kind. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(ConnectorConnectResultConnected), "connected")] +[JsonDerivedType(typeof(ConnectorConnectResultConsentRequired), "consent_required")] +[JsonDerivedType(typeof(ConnectorConnectResultPending), "pending")] +public partial class ConnectorConnectResult +{ + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} + + +/// The service is connected and the session MCP graph was reconciled. +/// The connected variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class ConnectorConnectResultConnected : ConnectorConnectResult +{ + /// + [JsonIgnore] + public override string Kind => "connected"; + + /// Fresh authoritative Connector state after MCP reconciliation. + [JsonPropertyName("status")] + public required ConnectorStatus Status { get; set; } +} + +/// Host-owned consent is required before bounded continuation can complete. +/// The consent_required variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class ConnectorConnectResultConsentRequired : ConnectorConnectResult +{ + /// + [JsonIgnore] + public override string Kind => "consent_required"; + + /// Validated HTTPS consent URL. The runtime does not open it. + [JsonPropertyName("consentUrl")] + public required string ConsentUrl { get; set; } + + /// Opaque ID accepted by continueConnection. + [JsonPropertyName("continuationId")] + public required string ContinuationId { get; set; } +} + +/// The service is still completing the connection without a consent URL. +/// The pending variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class ConnectorConnectResultPending : ConnectorConnectResult +{ + /// + [JsonIgnore] + public override string Kind => "pending"; + + /// Opaque ID accepted by continueConnection. + [JsonPropertyName("continuationId")] + public required string ContinuationId { get; set; } +} + +/// Selects one Connector and the pinned host-owned account used for its service and MCP authorization. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ConnectorConnectRequest +{ + /// Opaque account selection ID. It must match the account already pinned to the session, if any. + [JsonPropertyName("accountId")] + public string AccountId { get; set; } = string.Empty; + + /// Canonical Connector name from the current catalog. + [JsonPropertyName("connectorName")] + public string ConnectorName { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Explicitly bounded continuation of a pending Connector connection. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ConnectorContinueRequest +{ + /// Opaque continuation ID returned by connect, reconnect, or an earlier continuation. + [JsonPropertyName("continuationId")] + public string ContinuationId { get; set; } = string.Empty; + + /// Maximum wall-clock duration in milliseconds for this call. Must be between one and the capability limit. + [JsonPropertyName("deadlineMs")] + public int DeadlineMs { get; set; } + + /// Maximum catalog requests made by this call. Must be between one and the capability limit. + [JsonPropertyName("maxAttempts")] + public int MaxAttempts { get; set; } + + /// Delay in milliseconds between attempts. Must not exceed the capability limit. + [JsonPropertyName("pollIntervalMs")] + public int PollIntervalMs { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Authoritative result after disconnect and MCP reconciliation. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ConnectorDisconnectResult +{ + /// Whether the service accepted the idempotent disconnect. + [JsonPropertyName("disconnected")] + public bool Disconnected { get; set; } + + /// Fresh authoritative session state after removing Connector-owned MCP servers. + [JsonPropertyName("status")] + public ConnectorStatus Status { get => field ??= new(); set; } +} + +/// Requests authoritative Connector-to-MCP reconciliation for the pinned account. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ConnectorReconcileRequest +{ + /// Opaque account selection ID. It must match the account already pinned to the session, if any. + [JsonPropertyName("accountId")] + public required string AccountId { get; set; } + + /// Optional Connector name to reinitialize. Requires the targetedReconcile capability. + [JsonPropertyName("forceConnectorName")] + public string? ForceConnectorName { get; set; } + + /// When true, refresh the catalog before reconciling. A disabled Connector API performs no service request. + [JsonPropertyName("refreshCatalog")] + public bool? RefreshCatalog { get; set; } +} + +/// Requests authoritative Connector-to-MCP reconciliation for the pinned account. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ConnectorReconcileRequestWithSession +{ + /// Opaque account selection ID. It must match the account already pinned to the session, if any. + [JsonPropertyName("accountId")] + public string AccountId { get; set; } = string.Empty; + + /// Optional Connector name to reinitialize. Requires the targetedReconcile capability. + [JsonPropertyName("forceConnectorName")] + public string? ForceConnectorName { get; set; } + + /// When true, refresh the catalog before reconciling. A disabled Connector API performs no service request. + [JsonPropertyName("refreshCatalog")] + public bool? RefreshCatalog { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionConnectorsWithdrawProjectionRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionManagedSettingsGetRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Session plugin metadata, with name, marketplace, optional version, and enabled state. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class Plugin +{ + /// Opaque stable identity for a direct plugin source. + [JsonPropertyName("directSourceId")] + public string? DirectSourceId { get; set; } + + /// Whether the plugin is currently enabled. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } + + /// Whether this managed desired plugin has an installed or live record. + [JsonPropertyName("installed")] + public bool? Installed { get; set; } + + /// Absolute marketplace directory for a live plugin. + [JsonPropertyName("installedFrom")] + public string? InstalledFrom { get; set; } + + /// Whether enterprise managed settings control this plugin. + [JsonPropertyName("managed")] + public bool? Managed { get; set; } + + /// Enabled state required by enterprise managed settings. + [JsonPropertyName("managedDesiredEnabled")] + public bool? ManagedDesiredEnabled { get; set; } + + /// Marketplace the plugin came from. + [JsonPropertyName("marketplace")] + public string Marketplace { get; set; } = string.Empty; + + /// Plugin name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Runtime plugin provenance, such as "builtin". + [JsonPropertyName("source")] + public string? Source { get; set; } + + /// Installed version. + [JsonPropertyName("version")] + public string? Version { get; set; } +} + +/// Plugins installed for the session, with their enabled state and version metadata. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PluginList +{ + /// Installed plugins. + [JsonPropertyName("plugins")] + public IList Plugins { get => field ??= []; set; } +} + +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionPluginsListRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Plugin source resolved relative to the session's authoritative working directory. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionPluginsInstallRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; + + /// Plugin install spec. Accepts the same forms as the CLI: "plugin@marketplace" (marketplace install), "owner/repo" or "owner/repo:subpath" (GitHub direct), an http/https/ssh URL, or a local path. Direct (non-marketplace) installs are deprecated and will produce a deprecationWarning in the result. + [JsonPropertyName("source")] + public string Source { get; set; } = string.Empty; +} + +/// Name (or spec) of the plugin to uninstall. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class PluginsUninstallRequestWithSession +{ + /// Stable source identity for a direct (non-marketplace) install. Disambiguates uninstall when multiple installed plugins share the same name. + [JsonPropertyName("directSourceId")] + public string? DirectSourceId { get; set; } + + /// Plugin name or "plugin@marketplace" spec to uninstall. When ambiguous, prefer the fully-qualified spec. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Name (or spec) of the plugin to update. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class PluginsUpdateRequestWithSession +{ + /// Plugin name or "plugin@marketplace" spec to update. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Plugin names (or specs) to enable in the session's authoritative working directory. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionPluginsEnableRequest +{ + /// Plugin names or "plugin@marketplace" specs to enable. Unknown names are ignored. Non-marketplace direct installs are always enabled and cannot be toggled via this API. + [JsonPropertyName("names")] + public IList Names { get => field ??= []; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Plugin names (or specs) to disable in the session's authoritative working directory. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionPluginsDisableRequest +{ + /// Plugin names or "plugin@marketplace" specs to disable. Unknown names are ignored. Non-marketplace direct installs cannot be disabled via this API; uninstall them instead. Plugin-owned MCP servers are stopped in active sessions immediately; other plugin contributions remain available until each session reloads plugins. + [JsonPropertyName("names")] + public IList Names { get => field ??= []; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// RPC data type for SessionPluginsReload operations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionPluginsReloadRequest +{ + /// When true, skip repo-level hooks during the hook reload. Use before folder trust is confirmed; load them post-trust via `sessions.loadDeferredRepoHooks`. + [JsonPropertyName("deferRepoHooks")] + public bool? DeferRepoHooks { get; set; } + + /// Re-run custom-agent discovery after refreshing plugins. Defaults to true. + [JsonPropertyName("reloadCustomAgents")] + public bool? ReloadCustomAgents { get; set; } + + /// Re-discover and relaunch subprocess extensions (including plugin-shipped extensions) after refreshing plugins. Defaults to true. Has no effect when the session has no active extension controller (e.g. extensions were not requested for the session). + [JsonPropertyName("reloadExtensions")] + public bool? ReloadExtensions { get; set; } + + /// Re-load user, plugin, and (subject to `deferRepoHooks`) repo hooks. Defaults to true. Has no effect when the host has not registered a hook reloader (e.g. remote sessions). + [JsonPropertyName("reloadHooks")] + public bool? ReloadHooks { get; set; } + + /// Reload MCP server connections after refreshing plugins. Defaults to true. + [JsonPropertyName("reloadMcp")] + public bool? ReloadMcp { get; set; } +} + +/// RPC data type for SessionPluginsReloadRequestWithSession operations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionPluginsReloadRequestWithSession +{ + /// When true, skip repo-level hooks during the hook reload. Use before folder trust is confirmed; load them post-trust via `sessions.loadDeferredRepoHooks`. + [JsonPropertyName("deferRepoHooks")] + public bool? DeferRepoHooks { get; set; } + + /// Re-run custom-agent discovery after refreshing plugins. Defaults to true. + [JsonPropertyName("reloadCustomAgents")] + public bool? ReloadCustomAgents { get; set; } + + /// Re-discover and relaunch subprocess extensions (including plugin-shipped extensions) after refreshing plugins. Defaults to true. Has no effect when the session has no active extension controller (e.g. extensions were not requested for the session). + [JsonPropertyName("reloadExtensions")] + public bool? ReloadExtensions { get; set; } + + /// Re-load user, plugin, and (subject to `deferRepoHooks`) repo hooks. Defaults to true. Has no effect when the host has not registered a hook reloader (e.g. remote sessions). + [JsonPropertyName("reloadHooks")] + public bool? ReloadHooks { get; set; } + + /// Reload MCP server connections after refreshing plugins. Defaults to true. + [JsonPropertyName("reloadMcp")] + public bool? ReloadMcp { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Retry outcome for one plugin required by managed settings. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ManagedPluginRetryEntry +{ + /// Why the plugin was not prepared. Present when `status` is `failed` or `deferred`. + [JsonPropertyName("error")] + public string? Error { get; set; } + + /// Plugin spec in "plugin@marketplace" form. + [JsonPropertyName("spec")] + public string Spec { get; set; } = string.Empty; + + /// What the retry did for this plugin. + [JsonPropertyName("status")] + public ManagedPluginRetryStatus Status { get; set; } +} + +/// Per-plugin outcomes of retrying the plugins required by the session's managed settings. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionPluginsRetryManagedResult +{ + /// One entry per requested plugin, in request order. When no plugins were requested, one entry per plugin the managed settings require. + [JsonPropertyName("plugins")] + public IList Plugins { get => field ??= []; set; } +} + +/// RPC data type for SessionPluginsRetryManaged operations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionPluginsRetryManagedRequest +{ + /// "plugin@marketplace" specs to retry. Omit to retry every plugin the session's managed settings require. + [JsonPropertyName("plugins")] + public IList? Plugins { get; set; } +} + +/// RPC data type for SessionPluginsRetryManagedRequestWithSession operations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionPluginsRetryManagedRequestWithSession +{ + /// "plugin@marketplace" specs to retry. Omit to retry every plugin the session's managed settings require. + [JsonPropertyName("plugins")] + public IList? Plugins { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionPluginsMarketplacesListRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Marketplace source and optional working directory for relative-path resolution. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class PluginsMarketplacesAddRequestWithSession +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; + + /// Marketplace source. Accepts the same forms as the CLI: "owner/repo" or "owner/repo#ref" (GitHub), an http/https/ssh URL (optionally with #ref), a git scp-style URL (user@host:path), or a local path. The marketplace's own name (from its manifest) is used as the registration key. + [JsonPropertyName("source")] + public string Source { get; set; } = string.Empty; + + /// Working directory used to resolve relative local paths in `source`. Defaults to the server's current working directory. + [JsonPropertyName("workingDirectory")] + public string? WorkingDirectory { get; set; } +} + +/// Name of the marketplace to remove and an optional force flag. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class PluginsMarketplacesRemoveRequestWithSession +{ + /// When true, also uninstall every plugin sourced from this marketplace. When false (default), removal is a no-op if any plugin from this marketplace is installed and the dependent plugin names are returned in the result. + [JsonPropertyName("force")] + public bool? Force { get; set; } + + /// Marketplace name to remove. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Name of the marketplace whose plugin catalog to fetch. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class PluginsMarketplacesBrowseRequestWithSession +{ + /// Marketplace name to browse. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// RPC data type for SessionPluginsMarketplacesRefresh operations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionPluginsMarketplacesRefreshRequest +{ + /// Marketplace name to refresh. When omitted, every registered marketplace is refreshed. + [JsonPropertyName("name")] + public string? Name { get; set; } +} + +/// RPC data type for SessionPluginsMarketplacesRefreshRequestWithSession operations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionPluginsMarketplacesRefreshRequestWithSession +{ + /// Marketplace name to refresh. When omitted, every registered marketplace is refreshed. + [JsonPropertyName("name")] + public string? Name { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Short-lived, rotating credential the caller must send on every request, in addition to `apiKey` if one is present. Omitted when the endpoint does not require one. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ProviderSessionToken +{ + /// When the token expires, if known. Callers should refresh by calling `getEndpoint` again before this time, or reactively on any 401/403 response from `baseUrl`. + [JsonPropertyName("expiresAt")] + public DateTimeOffset? ExpiresAt { get; set; } + + /// HTTP header name the token must be sent under. + [JsonPropertyName("header")] + public string Header { get; set; } = string.Empty; + + /// The model the token is bound to, when applicable. When set, the token is only valid for requests against this model. + [JsonPropertyName("model")] + public string? Model { get; set; } + + /// The short-lived token value. + [JsonPropertyName("token")] + public string Token { get; set; } = string.Empty; +} + +/// A snapshot of the provider endpoint the session is currently configured to talk to. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ProviderEndpoint +{ + /// A credential the caller should use with this endpoint. Omitted only when the endpoint accepts unauthenticated requests. + [JsonPropertyName("apiKey")] + public string? ApiKey { get; set; } + + /// Base URL to pass to the LLM client library. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("baseUrl")] + public string BaseUrl { get; set; } = string.Empty; + + /// HTTP headers the caller must include on every outbound request. + [JsonPropertyName("headers")] + public IDictionary Headers { get => field ??= new Dictionary(); set; } + + /// Short-lived, rotating credential the caller must send on every request, in addition to `apiKey` if one is present. Omitted when the endpoint does not require one. + [JsonPropertyName("sessionToken")] + public ProviderSessionToken? SessionToken { get; set; } + + /// Transport to be used for provider requests. + [JsonPropertyName("transport")] + public ProviderEndpointTransport? Transport { get; set; } + + /// Provider family. Matches the `type` field of a BYOK provider config. + [JsonPropertyName("type")] + public ProviderEndpointType Type { get; set; } + + /// Wire API to be used, when required for the provider type. + [JsonPropertyName("wireApi")] + public ProviderEndpointWireApi? WireApi { get; set; } +} + +/// RPC data type for SessionProviderGetEndpoint operations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionProviderGetEndpointRequest +{ + /// Model identifier the caller intends to use against the returned endpoint. Used to pick the correct wire shape. Omit to use whichever model the session is currently using. + [JsonPropertyName("modelId")] + public string? ModelId { get; set; } +} + +/// RPC data type for SessionProviderGetEndpointRequestWithSession operations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionProviderGetEndpointRequestWithSession +{ + /// Model identifier the caller intends to use against the returned endpoint. Used to pick the correct wire shape. Omit to use whichever model the session is currently using. + [JsonPropertyName("modelId")] + public string? ModelId { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// The selectable model entries synthesized for the models added by this call. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ProviderAddResult +{ + /// Synthesized selectable model entries for the newly added BYOK models, each under its provider-qualified selection id (`provider/id`). Empty when only providers were added. + [JsonPropertyName("models")] + public IList Models { get => field ??= []; set; } +} + +/// BYOK providers and/or models to add to the session's registry at runtime. Both fields are optional; provide providers, models, or both. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ProviderAddRequest +{ + /// BYOK model definitions to register. Each must reference a provider that is already registered or included in this same call. Selection ids (`provider/id`) must be unique across the registry. + [JsonPropertyName("models")] + public IList? Models { get; set; } + + /// Named BYOK provider connections to register, additive to any providers already in the registry. Each name must be unique across the registry and must not contain '/'. + [JsonPropertyName("providers")] + public IList? Providers { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// The selectable model entries and selection ids synthesized for the synchronized BYOK models. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ProviderSyncResult +{ + /// True when synchronization withdrew the selected host-managed model, leaving the session with no explicit selection, so ordinary model resolution picks the session default. Synchronization never promotes a surviving host model in its place: publishing a model offers it, and the choice of which model to use stays with the user. + [JsonPropertyName("modelDeselected")] + public bool? ModelDeselected { get; set; } + + /// Synthesized selectable model entries for the synchronized BYOK models. + [JsonPropertyName("models")] + public IList Models { get => field ??= []; set; } + + /// Provider-qualified model selection ids present after synchronization. + [JsonPropertyName("selectionIds")] + public IList SelectionIds { get => field ??= []; set; } +} + +/// Authoritative BYOK provider and model registry snapshot to apply atomically to the session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ProviderSyncRequest +{ + /// BYOK model definition snapshot. Models absent from this list are removed. + [JsonPropertyName("models")] + public IList? Models { get; set; } + + /// Named BYOK provider connection snapshot. Providers absent from this list are removed. + [JsonPropertyName("providers")] + public IList? Providers { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// What the withdrawal actually removed from the registry. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ProviderWithdrawResult +{ + /// True when withdrawal removed the selected host-managed model, leaving the session with no explicit selection, so ordinary model resolution picks the session default. Withdrawal never promotes a surviving model in its place: the choice of which model to use stays with the user. + [JsonPropertyName("modelDeselected")] + public bool? ModelDeselected { get; set; } + + /// Providers removed because one of the withdrawn models was the last entry referencing them. A provider that merely has no models is not removed. + [JsonPropertyName("providersRemoved")] + public IList ProvidersRemoved { get => field ??= []; set; } + + /// Selection ids that were registered and are now withdrawn. Excludes requested ids that were not present. + [JsonPropertyName("withdrawn")] + public IList Withdrawn { get => field ??= []; set; } +} + +/// Host-managed model selection ids to withdraw from the session's BYOK registry. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ProviderWithdrawRequest +{ + /// Provider-qualified selection ids to withdraw. Ids that are not registered are ignored, so withdrawal is idempotent. A provider left with no models referencing it is removed too. + [JsonPropertyName("models")] + public IList Models { get => field ??= []; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Indicates whether the session options patch was applied successfully. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionUpdateOptionsResult +{ + /// Number of hooks loaded from installed plugins, returned when installedPlugins is updated. + [JsonPropertyName("pluginHookCount")] + public long? PluginHookCount { get; set; } + + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } +} + +/// Source descriptor for a `session.options.update` content-exclusion rule, with source name and type. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class OptionsUpdateAdditionalContentExclusionPolicyRuleSource +{ + /// Name of the policy source. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Type of the policy source. + [JsonPropertyName("type")] + public string Type { get; set; } = string.Empty; +} + +/// Single content-exclusion rule supplied to `session.options.update`, with paths, match conditions, and source. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class OptionsUpdateAdditionalContentExclusionPolicyRule +{ + /// Conditions of which at least one must match. + [JsonPropertyName("ifAnyMatch")] + public IList? IfAnyMatch { get; set; } + + /// Conditions none of which may match. + [JsonPropertyName("ifNoneMatch")] + public IList? IfNoneMatch { get; set; } + + /// Path patterns covered by this rule. + [JsonPropertyName("paths")] + public IList Paths { get => field ??= []; set; } + + /// Source descriptor for a `session.options.update` content-exclusion rule, with source name and type. + [JsonPropertyName("source")] + public OptionsUpdateAdditionalContentExclusionPolicyRuleSource Source { get => field ??= new(); set; } +} + +/// Content-exclusion policy supplied to `session.options.update`, with rules, last-updated data, and scope. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class OptionsUpdateAdditionalContentExclusionPolicy +{ + /// Opaque policy update timestamp supplied by the host. + [JsonPropertyName("last_updated_at")] + public JsonElement LastUpdatedAt { get; set; } + + /// Content-exclusion rules to apply. + [JsonPropertyName("rules")] + public IList Rules { get => field ??= []; set; } + + /// Allowed values for the `OptionsUpdateAdditionalContentExclusionPolicyScope` enumeration. + [JsonPropertyName("scope")] + public OptionsUpdateAdditionalContentExclusionPolicyScope Scope { get; set; } +} + +/// Options scoped to the built-in CAPI (Copilot API) provider. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class CapiSessionOptions +{ + /// Routing preference for sessions whose model is `auto`. On create or cold resume, this establishes the preference sent as `tier` on CAPI `/auto` requests; when omitted on cold resume, the runtime restores the last committed preference. On resident resume, a different value requests a safe switch after resume succeeds and cannot change an in-flight turn. Successful switches are persisted for later cold resume. When no preference is supplied or restored, CAPI default routing is used. `fast` is an integrator-only latency preset, not a first-party GitHub Copilot product preference. + [JsonPropertyName("autoTier")] + public AutoTier? AutoTier { get; set; } + + /// Whether to use WebSocket transport for the CAPI Responses API. Enabled by default when the model advertises `ws:/responses` support; set to `false` to force the HTTP Responses transport in environments where WebSockets are blocked (e.g. behind a proxy). Setting this to `false` is equivalent to the `COPILOT_CLI_DISABLE_WEBSOCKET_RESPONSES` environment variable. + [JsonPropertyName("enableWebSocketResponses")] + public bool? EnableWebSocketResponses { get; set; } +} + +/// Installed plugin record for a session, with marketplace, version, install time, enabled state, cache path, and source. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionInstalledPlugin +{ + /// Path where the plugin is cached locally. + [JsonPropertyName("cache_path")] + public string? CachePath { get; set; } + + /// Whether the plugin is currently enabled. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } + + /// Installation timestamp (ISO-8601). + [JsonPropertyName("installed_at")] + public string InstalledAt { get; set; } = string.Empty; + + /// Absolute path of the marketplace directory a live plugin was resolved from. Present only on live, never-persisted records — those synthesized at session start for a directory/local marketplace, whose cache_path points at the real plugin directory on disk rather than a copy under the installed-plugins cache. Its presence is what marks a record as live, and no record carrying it is ever written to the persisted installedPlugins key. + [JsonPropertyName("installed_from")] + public string? InstalledFrom { get; set; } + + /// Marketplace the plugin came from (empty string for direct repo installs). + [JsonPropertyName("marketplace")] + public string Marketplace { get; set; } = string.Empty; + + /// Plugin name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Source descriptor for direct repo installs (when marketplace is empty). + [JsonPropertyName("source")] + public JsonElement? Source { get; set; } + + /// Per-plugin source fingerprint (a SHA-256 hash of the plugin's catalog source spec plus its resolved source subtree — NOT a Git commit SHA) captured at marketplace install/update time. Auto-update compares it against the freshly recomputed fingerprint to detect a content change that does not bump the version. Absent for pre-existing installs and for direct (non-marketplace) installs. + [JsonPropertyName("source_sha")] + public string? SourceSha { get; set; } + + /// Installed version, if known. + [JsonPropertyName("version")] + public string? Version { get; set; } +} + +/// Custom model-provider configuration (BYOK). +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ProviderConfig +{ + /// API key. Optional for local providers like Ollama. + [JsonPropertyName("apiKey")] + public string? ApiKey { get; set; } + + /// Azure-specific provider options. + [JsonPropertyName("azure")] + public ProviderConfigAzure? Azure { get; set; } + + /// API endpoint URL. + [JsonPropertyName("baseUrl")] + public string BaseUrl { get; set; } = string.Empty; + + /// Bearer token for authentication. Sets the Authorization header directly. Takes precedence over apiKey when both are set. + [JsonPropertyName("bearerToken")] + public string? BearerToken { get; set; } + + /// When true, the SDK client supplies bearer tokens on demand: the runtime calls the client-session `providerToken.getToken` callback before each request and applies the returned token as an `Authorization: Bearer <token>` header. This is the bearer/OAuth scheme used by Azure AD / managed-identity tokens and provider OAuth access tokens (including Anthropic's), not a provider-specific API-key header such as Anthropic's `x-api-key`. The token-acquiring function itself stays on the SDK side and is never serialized; only this flag crosses the wire. When set alongside `apiKey`/`bearerToken`, the callback takes precedence: the runtime applies the token returned by `providerToken.getToken` as the `Authorization: Bearer` header for each request and does not send the static credential. + [JsonPropertyName("hasBearerTokenProvider")] + public bool? HasBearerTokenProvider { get; set; } + + /// Custom HTTP headers to include in all outbound requests to the provider. + [JsonPropertyName("headers")] + public IDictionary? Headers { get; set; } + + /// Maximum context window tokens for the model. + [JsonPropertyName("maxContextWindowTokens")] + public double? MaxContextWindowTokens { get; set; } + + /// Maximum output tokens for the model. + [JsonPropertyName("maxOutputTokens")] + public double? MaxOutputTokens { get; set; } + + /// Maximum prompt/input tokens for the model. + [JsonPropertyName("maxPromptTokens")] + public double? MaxPromptTokens { get; set; } + + /// Overrides for model capabilities when they cannot be inferred from modelId. + [JsonPropertyName("modelCapabilities")] + public ModelCapabilitiesOverride? ModelCapabilities { get; set; } + + /// Well-known model ID used for capability lookup. When set, agent behavior config and token limits are inferred from this model. + [JsonPropertyName("modelId")] + public string? ModelId { get; set; } + + /// The product serving the model, reported in telemetry as `model_provider`. Set it when `type` alone cannot identify the product, such as Ollama or LM Studio behind an OpenAI-compatible endpoint. Only affects telemetry. + [JsonPropertyName("modelProvider")] + public ProviderConfigModelProvider? ModelProvider { get; set; } + + /// Provider name used for model and telemetry attribution. + [JsonPropertyName("providerName")] + public string? ProviderName { get; set; } + + /// Provider transport. Defaults to "http". + [JsonPropertyName("transport")] + public ProviderConfigTransport? Transport { get; set; } + + /// Provider type. Defaults to "openai" for generic OpenAI-compatible APIs. + [JsonPropertyName("type")] + public ProviderConfigType? Type { get; set; } + + /// Wire API format (openai/azure only). Defaults to "completions". + [JsonPropertyName("wireApi")] + public ProviderConfigWireApi? WireApi { get; set; } + + /// The model identifier sent to the provider API for inference (the "wire" model), as opposed to modelId which is the well-known base. + [JsonPropertyName("wireModel")] + public string? WireModel { get; set; } +} + +/// +/// Command-scoped GitHub credential injection for the shell commands an agent runs. +/// +/// Each channel is opt-in and independent, and injection is scoped to the individual command +/// spawn: the credential is resolved from the session's *current* authentication at every spawn +/// and reaches only spawns whose script actually invokes `git` or `gh`. Because nothing is +/// retained between spawns, replacing the session credential (`session.gitHubAuth.setCredentials`) +/// changes what the next spawned command presents — which seeding a credential into the runtime +/// process's own environment cannot do, since a child's environment is fixed at `exec`. +/// +/// The credential is matched to the host it authenticates to, so a github.com credential is never +/// presented to a GitHub Enterprise host and vice versa. Where a channel cannot express that +/// boundary it injects nothing rather than crossing it -- see `gh` below. +/// +/// This is independent of `sandboxConfig`: it is a decision about which identity the agent +/// presents, not about what the agent may touch, and it works on every platform whether or not +/// an OS sandboxing backend is available. `sandboxConfig.auth` remains the sandbox-scoped +/// spelling and is additive with this one. +/// +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ShellCredentials +{ + /// + /// Whether to authenticate the agent's `gh` commands as the session's GitHub credential, by + /// exporting `GH_TOKEN` to a spawn that runs `gh`. Any inherited `gh` credential is removed from + /// spawns that do not, so the credential stays command-scoped. + /// + /// Applies to a github.com credential only. `gh` picks its credential variable from the host a + /// command targets rather than the one the credential belongs to, and the command can choose that + /// target, so `GH_ENTERPRISE_TOKEN` would offer a single-tenant enterprise credential to every + /// other enterprise host. A session whose credential is enterprise-scoped therefore runs `gh` + /// unauthenticated; its `git` commands are unaffected, because `http.<host>.extraheader` is scoped + /// to one host by construction. Default: false (opt-in). + /// + [JsonPropertyName("gh")] + public bool? Gh { get; set; } + + /// + /// Whether to authenticate the agent's `git` commands as the session's GitHub credential, by + /// injecting an `http.<host>.extraheader` (plus `insteadOf` rewrites so SSH-spelled remotes for + /// that host use the authenticated HTTPS transport). Applied only to a spawn that runs a + /// remote-contacting `git` subcommand. Default: false (opt-in). + /// + [JsonPropertyName("git")] + public bool? Git { get; set; } +} + +/// A host-provided script sourced before each built-in shell command when its shell target matches the active shell. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ShellInitScript +{ + /// Path to the script to source. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + + /// Built-in shell that may source this script. + [JsonPropertyName("shell")] + public ShellInitScriptShell Shell { get; set; } +} + +/// Per-session settings for built-in shell tools. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ShellOptions +{ + /// Command-scoped GitHub credential injection for shell commands. + [JsonPropertyName("credentials")] + public ShellCredentials? Credentials { get; set; } + + /// Controls automatic non-interactive profile loading where supported. Explicit initScripts are unaffected. + [JsonPropertyName("initProfile")] + public ShellInitProfile? InitProfile { get; set; } + + /// + /// Ordered host-provided script paths sourced before each built-in shell command when the + /// entry's shell target matches the active shell. Use these for rc files, environment setup scripts, + /// or other custom scripts. A script that returns a nonzero status is reported, and later scripts + /// and the user command continue while the shell remains running. Because scripts are sourced into + /// the command shell, `exit`, `exec`, failures under `set -e`, or other shell-terminating behavior + /// can prevent continuation. Script standard output is preserved; Bash script stderr is discarded, + /// PowerShell exception messages are replaced, and runtime-generated failure notices omit + /// configured script paths. When sandboxing is enabled, each script must already be readable under + /// the active sandbox filesystem policy. Pass an empty array to clear the list. + /// + [JsonPropertyName("initScripts")] + public IList? InitScripts { get; set; } + + /// + /// Flags passed to the active built-in shell process on startup, replacing its default flags. + /// When omitted, the built-in Bash shell uses `--norc --noprofile`, + /// and the built-in PowerShell shell uses `-NoProfile -NoLogo`. + /// + [JsonPropertyName("processFlags")] + public IList? ProcessFlags { get; set; } +} + +/// Patch of mutable session options to apply to the running session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionUpdateOptionsParams +{ + /// Additional content-exclusion policies to merge into the session's policy set. + [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] + [JsonPropertyName("additionalContentExclusionPolicies")] + public IList? AdditionalContentExclusionPolicies { get; set; } + + /// Runtime context discriminator (e.g., `cli`, `actions`). + [JsonPropertyName("agentContext")] + public string? AgentContext { get; set; } + + /// Explicit discovery-only agent roots, separate from customAgents and filesystem grants. Available without enableConfigDiscovery, subject to managed/source disables. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. + [JsonPropertyName("agentDirectories")] + public IList? AgentDirectories { get; set; } + + /// Whether to include instructions from every MCP server in the system prompt instead of only allowlisted servers. + [JsonPropertyName("allowAllMcpServerInstructions")] + public bool? AllowAllMcpServerInstructions { get; set; } + + /// Whether to disable the `ask_user` tool (encourages autonomous behavior). + [JsonPropertyName("askUserDisabled")] + public bool? AskUserDisabled { get; set; } + + /// Allowlist of tool names available to this session. + [JsonPropertyName("availableTools")] + public IList? AvailableTools { get; set; } + + /// Options scoped to the built-in CAPI (Copilot API) provider. + [JsonPropertyName("capi")] + public CapiSessionOptions? Capi { get; set; } + + /// Identifier of the client driving the session. + [JsonPropertyName("clientName")] + public string? ClientName { get; set; } + + /// Whether to include the `Co-authored-by` trailer in commit messages. + [JsonPropertyName("coauthorEnabled")] + public bool? CoauthorEnabled { get; set; } + + /// Context tier for models with tiered pricing. The session uses this to derive effective `modelCapabilitiesOverrides` so compaction, truncation, token display, and request limits honor the selected tier. + [JsonPropertyName("contextTier")] + public OptionsUpdateContextTier? ContextTier { get; set; } + + /// Whether to allow auto-mode continuation across turns. + [JsonPropertyName("continueOnAutoMode")] + public bool? ContinueOnAutoMode { get; set; } + + /// Override URL for the Copilot API endpoint. + [JsonPropertyName("copilotUrl")] + public string? CopilotUrl { get; set; } + + /// Whether to default custom agents to local-only execution. + [JsonPropertyName("customAgentsLocalOnly")] + public bool? CustomAgentsLocalOnly { get; set; } + + /// Instruction source IDs to exclude from the system prompt. + [JsonPropertyName("disabledInstructionSources")] + public IList? DisabledInstructionSources { get; set; } + + /// Skill IDs that should be excluded from this session. + [JsonPropertyName("disabledSkills")] + public IList? DisabledSkills { get; set; } + + /// Whether to enable repository-origin file hooks from `.github/hooks/` and trusted repository-configured roots. Does not control explicit hookDirectories or SDK callback hooks. + [JsonPropertyName("enableFileHooks")] + public bool? EnableFileHooks { get; set; } + + /// Whether to enable host git operations (context resolution, child repo scanning, git info in system prompt). + [JsonPropertyName("enableHostGitOperations")] + public bool? EnableHostGitOperations { get; set; } + + /// Whether to discover custom instructions on demand after successful file views (AGENTS.md / CLAUDE.md / .github/copilot-instructions.md surfacing). Combined with `skipCustomInstructions`. + [JsonPropertyName("enableOnDemandInstructionDiscovery")] + public bool? EnableOnDemandInstructionDiscovery { get; set; } + + /// Whether to surface reasoning-summary events from the model. + [JsonPropertyName("enableReasoningSummaries")] + public bool? EnableReasoningSummaries { get; set; } + + /// Whether shell-script safety heuristics are enabled. + [JsonPropertyName("enableScriptSafety")] + public bool? EnableScriptSafety { get; set; } + + /// Whether to enable cross-session store writes and reads. + [JsonPropertyName("enableSessionStore")] + public bool? EnableSessionStore { get; set; } + + /// Whether skill loading is enabled. Explicit false disables every source, including explicit SDK roots and a bound SDK provider; changing the value invalidates the loaded skill snapshot. When omitted, creation falls back to enableConfigDiscovery unless explicit SDK skill roots or an SDK skill provider are supplied. + [JsonPropertyName("enableSkills")] + public bool? EnableSkills { get; set; } + + /// Whether to stream model responses. + [JsonPropertyName("enableStreaming")] + public bool? EnableStreaming { get; set; } + + /// How env values are passed to MCP servers (`direct` inlines literal values; `indirect` resolves at launch). + [JsonPropertyName("envValueMode")] + public OptionsUpdateEnvValueMode? EnvValueMode { get; set; } + + /// Override directory for the session-events log. When unset, the runtime's default events log directory is used. + [JsonPropertyName("eventsLogDirectory")] + public string? EventsLogDirectory { get; set; } + + /// Whether subagent callback events should be forwarded into the session event log sink. + [JsonPropertyName("eventsLogIncludesSubagents")] + public bool? EventsLogIncludesSubagents { get; set; } + + /// Built-in subagent names to exclude from this session. Excluded built-ins are hidden from agent discovery and cannot be dispatched unless a custom agent with the same name is available. + [JsonPropertyName("excludedBuiltinAgents")] + public IList? ExcludedBuiltinAgents { get; set; } + + /// Denylist of tool names for this session. + [JsonPropertyName("excludedTools")] + public IList? ExcludedTools { get; set; } + + /// Map of feature-flag IDs to their boolean enabled state. + [JsonPropertyName("featureFlags")] + public IDictionary? FeatureFlags { get; set; } + + /// Explicit discovery-only file-hook roots, separate from callback hooks and filesystem grants. Available without enableConfigDiscovery or enableFileHooks, which controls repository-origin hooks only; hook/managed disables still apply. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. + [JsonPropertyName("hookDirectories")] + public IList? HookDirectories { get; set; } + + /// Skill scan directories and descendants excluded from discovery. Supports `~`-relative paths. + [JsonPropertyName("ignoredSkillsLocations")] + public IList? IgnoredSkillsLocations { get; set; } + + /// Built-in subagent names to include in this session. When specified, only these built-ins are available, subject to runtime availability and exclusions. Custom agents with the same name remain available. Set to null to remove the allowlist restriction. + [JsonPropertyName("includedBuiltinAgents")] + public IList? IncludedBuiltinAgents { get; set; } - /// Optional catalog logo. - [JsonPropertyName("logo")] - public string? Logo { get; set; } + /// Built-in skill names to include in this session. When specified, only these runtime-bundled skills are available. Skills from other sources with the same name remain available. Set to null to remove the allowlist restriction. + [JsonPropertyName("includedBuiltinSkills")] + public IList? IncludedBuiltinSkills { get; set; } - /// Canonical Connector name used by lifecycle methods. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Full set of installed plugins for the session. Replaces the existing list; the runtime invalidates the skills cache only when the list materially changes. + [JsonPropertyName("installedPlugins")] + public IList? InstalledPlugins { get; set; } - /// Optional catalog release tag. - [JsonPropertyName("releaseTag")] - public string? ReleaseTag { get; set; } + /// Explicit discovery-only instruction roots using existing SDK instruction formats, not filesystem grants. Available without enableConfigDiscovery, subject to instruction/managed disables. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. + [JsonPropertyName("instructionDirectories")] + public IList? InstructionDirectories { get; set; } - /// Opaque stable runtime IDs currently projected into the session for this Connector. - [JsonPropertyName("runtimeServerIds")] - public IList RuntimeServerIds { get => field ??= []; set; } + /// Stable integration identifier used for analytics and rate-limit attribution. + [JsonPropertyName("integrationId")] + public string? IntegrationId { get; set; } - /// Current authoritative service connection state. - [JsonPropertyName("status")] - public ConnectorCatalogStatus Status { get; set; } + /// Whether experimental capabilities are enabled. + [JsonPropertyName("isExperimentalMode")] + public bool? IsExperimentalMode { get; set; } - /// Optional catalog tier. - [JsonPropertyName("tier")] - public string? Tier { get; set; } -} + /// Whether interactive shell sessions are logged. + [JsonPropertyName("logInteractiveShells")] + public bool? LogInteractiveShells { get; set; } -/// Validated Connector catalog snapshot cached by the session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorCatalogResult -{ - /// Validated catalog entries in service order. - [JsonPropertyName("connectors")] - public IList Connectors { get => field ??= []; set; } + /// Identifier sent to LSP-style integrations. + [JsonPropertyName("lspClientName")] + public string? LspClientName { get; set; } - /// Unix epoch milliseconds when this snapshot was accepted. - [JsonPropertyName("refreshedAtMs")] - public long RefreshedAtMs { get; set; } + /// Whether to expose the `manage_schedule` tool to the agent. The runtime always owns the per-session schedule registry; this flag only controls tool exposure (typically gated to staff users). + [JsonPropertyName("manageScheduleEnabled")] + public bool? ManageScheduleEnabled { get; set; } - /// Monotonically increasing session-local catalog revision. - [JsonPropertyName("revision")] - public long Revision { get; set; } -} + /// Maximum decoded byte size of a single model-facing binary tool result (e.g. an image) persisted inline in session events and re-presented to the model on later turns / resume. Larger results are persisted as a metadata-only marker and shown to the model as a short text note. Defaults to 10 MB. + [JsonPropertyName("maxInlineBinaryBytes")] + public long? MaxInlineBinaryBytes { get; set; } -/// Live status of one session-owned MCP projection. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorRuntimeStatus -{ - /// Canonical Connector name that owns this server. - [JsonPropertyName("connectorName")] - public string ConnectorName { get; set; } = string.Empty; + /// The model ID to use for assistant turns. + [JsonPropertyName("model")] + public string? Model { get; set; } - /// Opaque runtime server ID. - [JsonPropertyName("runtimeServerId")] - public string RuntimeServerId { get; set; } = string.Empty; + /// Per-property model capability overrides for the selected model. + [JsonPropertyName("modelCapabilitiesOverrides")] + public ModelCapabilitiesOverride? ModelCapabilitiesOverrides { get; set; } - /// Current live MCP host status. - [JsonPropertyName("status")] - public ConnectorMcpStatus Status { get; set; } -} + /// Organization-level custom instructions to inject into the system prompt. + [JsonPropertyName("organizationCustomInstructions")] + public string? OrganizationCustomInstructions { get; set; } -/// Authoritative session connector state. Account IDs are opaque routing identifiers and credentials are never included. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorStatus -{ - /// Opaque account selection pinned to this session, when one has been selected. - [JsonPropertyName("accountId")] - public string? AccountId { get; set; } + /// Custom model-provider configuration (BYOK). + [JsonPropertyName("provider")] + public ProviderConfig? Provider { get; set; } - /// Connector API contract version. - [JsonPropertyName("apiVersion")] - public long ApiVersion { get; set; } + /// Reasoning effort for the selected model. CAPI values are model-defined and validated against the selected model; BYOK providers may define additional values. When omitted, no effort override is applied. + [JsonPropertyName("reasoningEffort")] + public string? ReasoningEffort { get; set; } - /// Exact selected account and stable scope requiring an authorization update, when proven by the Connector service. - [JsonPropertyName("authorizationRequirement")] - public ConnectorAuthorizationRequirement? AuthorizationRequirement { get; set; } + /// Reasoning summary mode for supported model clients. + [JsonPropertyName("reasoningSummary")] + public OptionsUpdateReasoningSummary? ReasoningSummary { get; set; } - /// Current feature and session availability. - [JsonPropertyName("availability")] - public ConnectorAvailability Availability { get; set; } + /// Whether the session is running in an interactive UI. + [JsonPropertyName("runningInInteractiveMode")] + public bool? RunningInInteractiveMode { get; set; } - /// Latest validated catalog snapshot, when available. - [JsonPropertyName("catalog")] - public ConnectorCatalogResult? Catalog { get; set; } + /// Resolved sandbox configuration. + [JsonPropertyName("sandboxConfig")] + public SandboxConfig? SandboxConfig { get; set; } - /// Number of active opaque connection continuations. - [JsonPropertyName("pendingConnections")] - public long PendingConnections { get; set; } + /// Origin of the sandbox choice. Settings-derived origins (never_configured, user_enabled, user_disabled, repository_policy) let managed policy floor a host preference; explicit below-floor changes remain policy conflicts unless a session opt-out is authorized. Also used for telemetry provenance. + [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] + [JsonPropertyName("sandboxConfigSource")] + public SandboxConfigSource? SandboxConfigSource { get; set; } - /// Live MCP status for every Connector-owned runtime server. - [JsonPropertyName("runtimeServers")] - public IList RuntimeServers { get => field ??= []; set; } -} + /// Replaces the session's capability set with the given list. Use to enable or disable capabilities mid-session (e.g., remove `memory` for reproducible scripted runs). Omit the field to leave the existing capability set unchanged. + [JsonPropertyName("sessionCapabilities")] + public IList? SessionCapabilities { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionConnectorsGetStatusRequest -{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; -} -/// Pins a Connector operation to one host-owned GitHub account through its opaque selection ID. Provider tokens are never accepted. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ConnectorAccountRequest -{ - /// Opaque account selection ID previously returned by an account discovery API. - [JsonPropertyName("accountId")] - public string AccountId { get; set; } = string.Empty; + /// Optional session limits. Pass null to clear the session limits. + [JsonPropertyName("sessionLimits")] + public SessionLimitsConfig? SessionLimits { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Per-session settings for built-in shell tools. + [JsonPropertyName("shell")] + public ShellOptions? Shell { get; set; } -/// Typed result of initiating or continuing a Connector connection. -/// Polymorphic base type discriminated by kind. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(ConnectorConnectResultConnected), "connected")] -[JsonDerivedType(typeof(ConnectorConnectResultConsentRequired), "consent_required")] -[JsonDerivedType(typeof(ConnectorConnectResultPending), "pending")] -public partial class ConnectorConnectResult -{ - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// Use shell.initProfile instead. Shell init profile (`None` or `NonInteractive`). + [EditorBrowsable(EditorBrowsableState.Never)] +#if NET5_0_OR_GREATER + [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] +#else + [Obsolete("This member is deprecated and will be removed in a future version.")] +#endif + [JsonPropertyName("shellInitProfile")] + public string? ShellInitProfile { get; set; } + /// PowerShell process flags applied to built-in and user-requested shell commands. + [JsonPropertyName("shellProcessFlags")] + public IList? ShellProcessFlags { get; set; } -/// The service is connected and the session MCP graph was reconciled. -/// The connected variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ConnectorConnectResultConnected : ConnectorConnectResult -{ - /// - [JsonIgnore] - public override string Kind => "connected"; + /// Explicit discovery-only skill roots, not filesystem grants. Available without enableConfigDiscovery, subject to skill/managed disables. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. + [JsonPropertyName("skillDirectories")] + public IList? SkillDirectories { get; set; } - /// Fresh authoritative Connector state after MCP reconciliation. - [JsonPropertyName("status")] - public required ConnectorStatus Status { get; set; } -} + /// Whether to skip loading custom instruction sources. + [JsonPropertyName("skipCustomInstructions")] + public bool? SkipCustomInstructions { get; set; } -/// Host-owned consent is required before bounded continuation can complete. -/// The consent_required variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ConnectorConnectResultConsentRequired : ConnectorConnectResult -{ - /// - [JsonIgnore] - public override string Kind => "consent_required"; + /// Whether to skip embedding retrieval pipeline initialization and execution. + [JsonPropertyName("skipEmbeddingRetrieval")] + public bool? SkipEmbeddingRetrieval { get; set; } - /// Validated HTTPS consent URL. The runtime does not open it. - [JsonPropertyName("consentUrl")] - public required string ConsentUrl { get; set; } + /// When true, the selected custom agent's prompt is not injected into the user message (skill context is still injected). Used by automation triggers where the agent prompt is already in the problem statement. + [JsonPropertyName("suppressCustomAgentPrompt")] + public bool? SuppressCustomAgentPrompt { get; set; } - /// Opaque ID accepted by continueConnection. - [JsonPropertyName("continuationId")] - public required string ContinuationId { get; set; } -} + /// Controls how availableTools (allowlist) and excludedTools (denylist) combine when both are set. + [JsonPropertyName("toolFilterPrecedence")] + public OptionsUpdateToolFilterPrecedence? ToolFilterPrecedence { get; set; } -/// The service is still completing the connection without a consent URL. -/// The pending variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ConnectorConnectResultPending : ConnectorConnectResult -{ - /// - [JsonIgnore] - public override string Kind => "pending"; + /// Optional path for trajectory output. + [JsonPropertyName("trajectoryFile")] + public string? TrajectoryFile { get; set; } - /// Opaque ID accepted by continueConnection. - [JsonPropertyName("continuationId")] - public required string ContinuationId { get; set; } + /// Output verbosity level for supported models. + [JsonPropertyName("verbosity")] + public Verbosity? Verbosity { get; set; } + + /// Absolute working-directory path for shell tools. + [JsonPropertyName("workingDirectory")] + public string? WorkingDirectory { get; set; } } -/// Selects one Connector and the pinned host-owned account used for its service and MCP authorization. +/// Parameters for (re)loading the merged LSP configuration set. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ConnectorConnectRequest +internal sealed class LspInitializeRequest { - /// Opaque account selection ID. It must match the account already pinned to the session, if any. - [JsonPropertyName("accountId")] - public string AccountId { get; set; } = string.Empty; + /// Force re-initialization even when LSP configs were already loaded for the working directory. + [JsonPropertyName("force")] + public bool? Force { get; set; } - /// Canonical Connector name from the current catalog. - [JsonPropertyName("connectorName")] - public string ConnectorName { get; set; } = string.Empty; + /// Git root used as the boundary when traversing for project-level LSP configs (supports monorepos). + [JsonPropertyName("gitRoot")] + public string? GitRoot { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Working directory used to load project-level LSP configs. Defaults to the session working directory when omitted. + [JsonPropertyName("workingDirectory")] + public string? WorkingDirectory { get; set; } } -/// Explicitly bounded continuation of a pending Connector connection. +/// Discovered extension metadata, including source-qualified ID, name, discovery source, status, and optional process ID. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ConnectorContinueRequest +public sealed class Extension { - /// Opaque continuation ID returned by connect, reconnect, or an earlier continuation. - [JsonPropertyName("continuationId")] - public string ContinuationId { get; set; } = string.Empty; + /// Source-qualified ID (e.g., 'project:my-ext', 'user:auth-helper', 'plugin:my-plugin:my-ext'). + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// Maximum wall-clock duration in milliseconds for this call. Must be between one and the capability limit. - [JsonPropertyName("deadlineMs")] - public int DeadlineMs { get; set; } + /// Extension name (directory name). + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Maximum catalog requests made by this call. Must be between one and the capability limit. - [JsonPropertyName("maxAttempts")] - public int MaxAttempts { get; set; } + /// Process ID if the extension is running. + [JsonPropertyName("pid")] + public long? Pid { get; set; } - /// Delay in milliseconds between attempts. Must not exceed the capability limit. - [JsonPropertyName("pollIntervalMs")] - public int PollIntervalMs { get; set; } + /// Discovery source: project (.github/extensions/), user (~/.copilot/extensions/), plugin (installed plugin), or session (session-state/<id>/extensions/). + [JsonPropertyName("source")] + public ExtensionSource Source { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Current status: running, disabled, failed, or starting. + [JsonPropertyName("status")] + public ExtensionStatus Status { get; set; } } -/// Authoritative result after disconnect and MCP reconciliation. +/// Extensions discovered for the session, with their current status. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorDisconnectResult +public sealed class ExtensionList { - /// Whether the service accepted the idempotent disconnect. - [JsonPropertyName("disconnected")] - public bool Disconnected { get; set; } - - /// Fresh authoritative session state after removing Connector-owned MCP servers. - [JsonPropertyName("status")] - public ConnectorStatus Status { get => field ??= new(); set; } + /// Discovered extensions and their current status. + [JsonPropertyName("extensions")] + public IList Extensions { get => field ??= []; set; } } -/// Requests authoritative Connector-to-MCP reconciliation for the pinned account. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorReconcileRequest +internal sealed class SessionExtensionsListRequest { - /// Opaque account selection ID. It must match the account already pinned to the session, if any. - [JsonPropertyName("accountId")] - public required string AccountId { get; set; } - - /// Optional Connector name to reinitialize. Requires the targetedReconcile capability. - [JsonPropertyName("forceConnectorName")] - public string? ForceConnectorName { get; set; } - - /// When true, refresh the catalog before reconciling. A disabled Connector API performs no service request. - [JsonPropertyName("refreshCatalog")] - public bool? RefreshCatalog { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Requests authoritative Connector-to-MCP reconciliation for the pinned account. +/// Source-qualified extension identifier to enable for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ConnectorReconcileRequestWithSession +internal sealed class ExtensionsEnableRequest { - /// Opaque account selection ID. It must match the account already pinned to the session, if any. - [JsonPropertyName("accountId")] - public string AccountId { get; set; } = string.Empty; - - /// Optional Connector name to reinitialize. Requires the targetedReconcile capability. - [JsonPropertyName("forceConnectorName")] - public string? ForceConnectorName { get; set; } - - /// When true, refresh the catalog before reconciling. A disabled Connector API performs no service request. - [JsonPropertyName("refreshCatalog")] - public bool? RefreshCatalog { get; set; } + /// Source-qualified extension ID to enable. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Identifies the target session. +/// Source-qualified extension identifier to disable for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionConnectorsWithdrawProjectionRequest +internal sealed class ExtensionsDisableRequest { + /// Source-qualified extension ID to disable. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; @@ -19140,2508 +22423,2419 @@ internal sealed class SessionConnectorsWithdrawProjectionRequest /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionManagedSettingsGetRequest +internal sealed class SessionExtensionsReloadRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Session plugin metadata, with name, marketplace, optional version, and enabled state. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class Plugin +internal sealed class SessionExtensionsReconcileRequest { - /// Opaque stable identity for a direct plugin source. - [JsonPropertyName("directSourceId")] - public string? DirectSourceId { get; set; } - - /// Whether the plugin is currently enabled. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } - - /// Whether this managed desired plugin has an installed or live record. - [JsonPropertyName("installed")] - public bool? Installed { get; set; } - - /// Absolute marketplace directory for a live plugin. - [JsonPropertyName("installedFrom")] - public string? InstalledFrom { get; set; } - - /// Whether enterprise managed settings control this plugin. - [JsonPropertyName("managed")] - public bool? Managed { get; set; } - - /// Enabled state required by enterprise managed settings. - [JsonPropertyName("managedDesiredEnabled")] - public bool? ManagedDesiredEnabled { get; set; } - - /// Marketplace the plugin came from. - [JsonPropertyName("marketplace")] - public string Marketplace { get; set; } = string.Empty; - - /// Plugin name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - /// Runtime plugin provenance, such as "builtin". - [JsonPropertyName("source")] - public string? Source { get; set; } - - /// Installed version. - [JsonPropertyName("version")] - public string? Version { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Plugins installed for the session, with their enabled state and version metadata. +/// Attachment union accepted by push input, covering files, directories, GitHub objects, blobs, snippets, and extension context. +/// Polymorphic base type discriminated by type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PluginList +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "type", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(PushAttachmentFile), "file")] +[JsonDerivedType(typeof(PushAttachmentDirectory), "directory")] +[JsonDerivedType(typeof(PushAttachmentSelection), "selection")] +[JsonDerivedType(typeof(PushAttachmentGitHubReference), "github_reference")] +[JsonDerivedType(typeof(PushAttachmentGitHubCommit), "github_commit")] +[JsonDerivedType(typeof(PushAttachmentGitHubRelease), "github_release")] +[JsonDerivedType(typeof(PushAttachmentGitHubActionsJob), "github_actions_job")] +[JsonDerivedType(typeof(PushAttachmentGitHubRepository), "github_repository")] +[JsonDerivedType(typeof(PushAttachmentGitHubFileDiff), "github_file_diff")] +[JsonDerivedType(typeof(PushAttachmentGitHubTreeComparison), "github_tree_comparison")] +[JsonDerivedType(typeof(PushAttachmentGitHubUrl), "github_url")] +[JsonDerivedType(typeof(PushAttachmentGitHubFile), "github_file")] +[JsonDerivedType(typeof(PushAttachmentGitHubSnippet), "github_snippet")] +[JsonDerivedType(typeof(PushAttachmentBlob), "blob")] +[JsonDerivedType(typeof(PushAttachmentExtensionContext), "extension_context")] +public partial class PushAttachment { - /// Installed plugins. - [JsonPropertyName("plugins")] - public IList Plugins { get => field ??= []; set; } + /// The type discriminator. + [JsonPropertyName("type")] + public virtual string Type { get; set; } = string.Empty; } -/// Identifies the target session. + +/// Optional line range to scope the attachment to a specific section of the file. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPluginsListRequest +public sealed class PushAttachmentFileLineRange { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// End line number (1-based, inclusive). + [JsonPropertyName("end")] + public long End { get; set; } + + /// Start line number (1-based). + [JsonPropertyName("start")] + public long Start { get; set; } } -/// Plugin source resolved relative to the session's authoritative working directory. +/// File attachment. +/// The file variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPluginsInstallRequest +public partial class PushAttachmentFile : PushAttachment { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Type => "file"; - /// Plugin install spec. Accepts the same forms as the CLI: "plugin@marketplace" (marketplace install), "owner/repo" or "owner/repo:subpath" (GitHub direct), an http/https/ssh URL, or a local path. Direct (non-marketplace) installs are deprecated and will produce a deprecationWarning in the result. - [JsonPropertyName("source")] - public string Source { get; set; } = string.Empty; + /// User-facing display name for the attachment. + [JsonPropertyName("displayName")] + public required string DisplayName { get; set; } + + /// Optional line range to scope the attachment to a specific section of the file. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("lineRange")] + public PushAttachmentFileLineRange? LineRange { get; set; } + + /// Absolute file path. + [JsonPropertyName("path")] + public required string Path { get; set; } } -/// Name (or spec) of the plugin to uninstall. +/// Directory attachment. +/// The directory variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsUninstallRequestWithSession +public partial class PushAttachmentDirectory : PushAttachment { - /// Stable source identity for a direct (non-marketplace) install. Disambiguates uninstall when multiple installed plugins share the same name. - [JsonPropertyName("directSourceId")] - public string? DirectSourceId { get; set; } + /// + [JsonIgnore] + public override string Type => "directory"; - /// Plugin name or "plugin@marketplace" spec to uninstall. When ambiguous, prefer the fully-qualified spec. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// User-facing display name for the attachment. + [JsonPropertyName("displayName")] + public required string DisplayName { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Absolute directory path. + [JsonPropertyName("path")] + public required string Path { get; set; } } -/// Name (or spec) of the plugin to update. +/// End position of the selection. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsUpdateRequestWithSession +public sealed class PushAttachmentSelectionDetailsEnd { - /// Plugin name or "plugin@marketplace" spec to update. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// End character offset within the line (0-based). + [JsonPropertyName("character")] + public long Character { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// End line number (0-based). + [JsonPropertyName("line")] + public long Line { get; set; } } -/// Plugin names (or specs) to enable in the session's authoritative working directory. +/// Start position of the selection. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPluginsEnableRequest +public sealed class PushAttachmentSelectionDetailsStart { - /// Plugin names or "plugin@marketplace" specs to enable. Unknown names are ignored. Non-marketplace direct installs are always enabled and cannot be toggled via this API. - [JsonPropertyName("names")] - public IList Names { get => field ??= []; set; } + /// Start character offset within the line (0-based). + [JsonPropertyName("character")] + public long Character { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Start line number (0-based). + [JsonPropertyName("line")] + public long Line { get; set; } } -/// Plugin names (or specs) to disable in the session's authoritative working directory. +/// Position range of the selection within the file. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPluginsDisableRequest +public sealed class PushAttachmentSelectionDetails { - /// Plugin names or "plugin@marketplace" specs to disable. Unknown names are ignored. Non-marketplace direct installs cannot be disabled via this API; uninstall them instead. Plugin-owned MCP servers are stopped in active sessions immediately; other plugin contributions remain available until each session reloads plugins. - [JsonPropertyName("names")] - public IList Names { get => field ??= []; set; } + /// End position of the selection. + [JsonPropertyName("end")] + public PushAttachmentSelectionDetailsEnd End { get => field ??= new(); set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Start position of the selection. + [JsonPropertyName("start")] + public PushAttachmentSelectionDetailsStart Start { get => field ??= new(); set; } } -/// RPC data type for SessionPluginsReload operations. +/// Code selection attachment from an editor. +/// The selection variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionPluginsReloadRequest +public partial class PushAttachmentSelection : PushAttachment { - /// When true, skip repo-level hooks during the hook reload. Use before folder trust is confirmed; load them post-trust via `sessions.loadDeferredRepoHooks`. - [JsonPropertyName("deferRepoHooks")] - public bool? DeferRepoHooks { get; set; } + /// + [JsonIgnore] + public override string Type => "selection"; - /// Re-run custom-agent discovery after refreshing plugins. Defaults to true. - [JsonPropertyName("reloadCustomAgents")] - public bool? ReloadCustomAgents { get; set; } + /// User-facing display name for the selection. + [JsonPropertyName("displayName")] + public required string DisplayName { get; set; } - /// Re-discover and relaunch subprocess extensions (including plugin-shipped extensions) after refreshing plugins. Defaults to true. Has no effect when the session has no active extension controller (e.g. extensions were not requested for the session). - [JsonPropertyName("reloadExtensions")] - public bool? ReloadExtensions { get; set; } + /// Absolute path to the file containing the selection. + [JsonPropertyName("filePath")] + public required string FilePath { get; set; } - /// Re-load user, plugin, and (subject to `deferRepoHooks`) repo hooks. Defaults to true. Has no effect when the host has not registered a hook reloader (e.g. remote sessions). - [JsonPropertyName("reloadHooks")] - public bool? ReloadHooks { get; set; } + /// Position range of the selection within the file. + [JsonPropertyName("selection")] + public required PushAttachmentSelectionDetails Selection { get; set; } - /// Reload MCP server connections after refreshing plugins. Defaults to true. - [JsonPropertyName("reloadMcp")] - public bool? ReloadMcp { get; set; } + /// The selected text content. + [JsonPropertyName("text")] + public required string Text { get; set; } } -/// RPC data type for SessionPluginsReloadRequestWithSession operations. +/// GitHub issue, pull request, or discussion reference. +/// The github_reference variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPluginsReloadRequestWithSession +public partial class PushAttachmentGitHubReference : PushAttachment { - /// When true, skip repo-level hooks during the hook reload. Use before folder trust is confirmed; load them post-trust via `sessions.loadDeferredRepoHooks`. - [JsonPropertyName("deferRepoHooks")] - public bool? DeferRepoHooks { get; set; } + /// + [JsonIgnore] + public override string Type => "github_reference"; - /// Re-run custom-agent discovery after refreshing plugins. Defaults to true. - [JsonPropertyName("reloadCustomAgents")] - public bool? ReloadCustomAgents { get; set; } + /// Issue, pull request, or discussion number. + [JsonPropertyName("number")] + public required long Number { get; set; } - /// Re-discover and relaunch subprocess extensions (including plugin-shipped extensions) after refreshing plugins. Defaults to true. Has no effect when the session has no active extension controller (e.g. extensions were not requested for the session). - [JsonPropertyName("reloadExtensions")] - public bool? ReloadExtensions { get; set; } + /// Type of GitHub reference. + [JsonPropertyName("referenceType")] + public required PushAttachmentGitHubReferenceType ReferenceType { get; set; } - /// Re-load user, plugin, and (subject to `deferRepoHooks`) repo hooks. Defaults to true. Has no effect when the host has not registered a hook reloader (e.g. remote sessions). - [JsonPropertyName("reloadHooks")] - public bool? ReloadHooks { get; set; } + /// Current state of the referenced item (e.g., open, closed, merged). + [JsonPropertyName("state")] + public required string State { get; set; } - /// Reload MCP server connections after refreshing plugins. Defaults to true. - [JsonPropertyName("reloadMcp")] - public bool? ReloadMcp { get; set; } + /// Title of the referenced item. + [JsonPropertyName("title")] + public required string Title { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// URL to the referenced item on GitHub. + [JsonPropertyName("url")] + public required string Url { get; set; } } -/// Identifies the target session. +/// Pointer to a GitHub repository. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPluginsMarketplacesListRequest +public sealed class PushGitHubRepoRef { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Numeric GitHub repository id. + [JsonPropertyName("id")] + public long? Id { get; set; } + + /// Repository name (without owner). + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Repository owner login (user or organization). + [JsonPropertyName("owner")] + public string Owner { get; set; } = string.Empty; } -/// Marketplace source and optional working directory for relative-path resolution. +/// Pointer to a GitHub commit. +/// The github_commit variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsMarketplacesAddRequestWithSession +public partial class PushAttachmentGitHubCommit : PushAttachment { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Type => "github_commit"; - /// Marketplace source. Accepts the same forms as the CLI: "owner/repo" or "owner/repo#ref" (GitHub), an http/https/ssh URL (optionally with #ref), a git scp-style URL (user@host:path), or a local path. The marketplace's own name (from its manifest) is used as the registration key. - [JsonPropertyName("source")] - public string Source { get; set; } = string.Empty; + /// First line of the commit message. + [JsonPropertyName("message")] + public required string Message { get; set; } - /// Working directory used to resolve relative local paths in `source`. Defaults to the server's current working directory. - [JsonPropertyName("workingDirectory")] - public string? WorkingDirectory { get; set; } + /// Full commit SHA. + [JsonPropertyName("oid")] + public required string Oid { get; set; } + + /// Repository the commit belongs to. + [JsonPropertyName("repo")] + public required PushGitHubRepoRef Repo { get; set; } + + /// URL to the commit on GitHub. + [JsonPropertyName("url")] + public required string Url { get; set; } } -/// Name of the marketplace to remove and an optional force flag. +/// Pointer to a GitHub release. +/// The github_release variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsMarketplacesRemoveRequestWithSession +public partial class PushAttachmentGitHubRelease : PushAttachment { - /// When true, also uninstall every plugin sourced from this marketplace. When false (default), removal is a no-op if any plugin from this marketplace is installed and the dependent plugin names are returned in the result. - [JsonPropertyName("force")] - public bool? Force { get; set; } + /// + [JsonIgnore] + public override string Type => "github_release"; - /// Marketplace name to remove. + /// Human-readable release name. [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + public required string Name { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Repository the release belongs to. + [JsonPropertyName("repo")] + public required PushGitHubRepoRef Repo { get; set; } -/// Name of the marketplace whose plugin catalog to fetch. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsMarketplacesBrowseRequestWithSession -{ - /// Marketplace name to browse. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Git tag the release is anchored to. + [JsonPropertyName("tagName")] + public required string TagName { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// URL to the release on GitHub. + [JsonPropertyName("url")] + public required string Url { get; set; } } -/// RPC data type for SessionPluginsMarketplacesRefresh operations. +/// Pointer to a GitHub Actions job. +/// The github_actions_job variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionPluginsMarketplacesRefreshRequest +public partial class PushAttachmentGitHubActionsJob : PushAttachment { - /// Marketplace name to refresh. When omitted, every registered marketplace is refreshed. - [JsonPropertyName("name")] - public string? Name { get; set; } -} + /// + [JsonIgnore] + public override string Type => "github_actions_job"; -/// RPC data type for SessionPluginsMarketplacesRefreshRequestWithSession operations. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPluginsMarketplacesRefreshRequestWithSession -{ - /// Marketplace name to refresh. When omitted, every registered marketplace is refreshed. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// Terminal conclusion of the job when finished (e.g., success, failure, cancelled). Absent for in-progress jobs. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("conclusion")] + public string? Conclusion { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Job id within the workflow run. + [JsonPropertyName("jobId")] + public required long JobId { get; set; } -/// Short-lived, rotating credential the caller must send on every request, in addition to `apiKey` if one is present. Omitted when the endpoint does not require one. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderSessionToken -{ - /// When the token expires, if known. Callers should refresh by calling `getEndpoint` again before this time, or reactively on any 401/403 response from `baseUrl`. - [JsonPropertyName("expiresAt")] - public DateTimeOffset? ExpiresAt { get; set; } + /// Display name of the job. + [JsonPropertyName("jobName")] + public required string JobName { get; set; } - /// HTTP header name the token must be sent under. - [JsonPropertyName("header")] - public string Header { get; set; } = string.Empty; + /// Repository the workflow run belongs to. + [JsonPropertyName("repo")] + public required PushGitHubRepoRef Repo { get; set; } - /// The model the token is bound to, when applicable. When set, the token is only valid for requests against this model. - [JsonPropertyName("model")] - public string? Model { get; set; } + /// URL to the job on GitHub. + [JsonPropertyName("url")] + public required string Url { get; set; } - /// The short-lived token value. - [JsonPropertyName("token")] - public string Token { get; set; } = string.Empty; + /// Display name of the workflow the job ran in. + [JsonPropertyName("workflowName")] + public required string WorkflowName { get; set; } } -/// A snapshot of the provider endpoint the session is currently configured to talk to. +/// Pointer to a GitHub repository. +/// The github_repository variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderEndpoint +public partial class PushAttachmentGitHubRepository : PushAttachment { - /// A credential the caller should use with this endpoint. Omitted only when the endpoint accepts unauthenticated requests. - [JsonPropertyName("apiKey")] - public string? ApiKey { get; set; } - - /// Base URL to pass to the LLM client library. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("baseUrl")] - public string BaseUrl { get; set; } = string.Empty; - - /// HTTP headers the caller must include on every outbound request. - [JsonPropertyName("headers")] - public IDictionary Headers { get => field ??= new Dictionary(); set; } + /// + [JsonIgnore] + public override string Type => "github_repository"; - /// Short-lived, rotating credential the caller must send on every request, in addition to `apiKey` if one is present. Omitted when the endpoint does not require one. - [JsonPropertyName("sessionToken")] - public ProviderSessionToken? SessionToken { get; set; } + /// Short description of the repository. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("description")] + public string? Description { get; set; } - /// Transport to be used for provider requests. - [JsonPropertyName("transport")] - public ProviderEndpointTransport? Transport { get; set; } + /// Git ref this attachment is anchored at (branch, tag, or commit). When absent the default branch is implied. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("ref")] + public string? Ref { get; set; } - /// Provider family. Matches the `type` field of a BYOK provider config. - [JsonPropertyName("type")] - public ProviderEndpointType Type { get; set; } + /// Repository pointer. + [JsonPropertyName("repo")] + public required PushGitHubRepoRef Repo { get; set; } - /// Wire API to be used, when required for the provider type. - [JsonPropertyName("wireApi")] - public ProviderEndpointWireApi? WireApi { get; set; } + /// URL to the repository on GitHub. + [JsonPropertyName("url")] + public required string Url { get; set; } } -/// RPC data type for SessionProviderGetEndpoint operations. +/// One side of a file diff (head or base). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionProviderGetEndpointRequest +public sealed class PushAttachmentGitHubFileDiffSide { - /// Model identifier the caller intends to use against the returned endpoint. Used to pick the correct wire shape. Omit to use whichever model the session is currently using. - [JsonPropertyName("modelId")] - public string? ModelId { get; set; } + /// Repository-relative path to the file. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + + /// Git ref (branch, tag, or commit SHA) the file is read at. + [JsonPropertyName("ref")] + public string Ref { get; set; } = string.Empty; + + /// Repository the file lives in. + [JsonPropertyName("repo")] + public PushGitHubRepoRef Repo { get => field ??= new(); set; } } -/// RPC data type for SessionProviderGetEndpointRequestWithSession operations. +/// Pointer to a single-file diff. At least one of `head` and `base` must be present. +/// The github_file_diff variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionProviderGetEndpointRequestWithSession +public partial class PushAttachmentGitHubFileDiff : PushAttachment { - /// Model identifier the caller intends to use against the returned endpoint. Used to pick the correct wire shape. Omit to use whichever model the session is currently using. - [JsonPropertyName("modelId")] - public string? ModelId { get; set; } + /// + [JsonIgnore] + public override string Type => "github_file_diff"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// File location on the base side of the diff. Absent for additions. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("base")] + public PushAttachmentGitHubFileDiffSide? Base { get; set; } + + /// File location on the head side of the diff. Absent for deletions. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("head")] + public PushAttachmentGitHubFileDiffSide? Head { get; set; } + + /// URL to the diff on GitHub (e.g., a commit, compare, or PR-file URL). + [JsonPropertyName("url")] + public required string Url { get; set; } } -/// The selectable model entries synthesized for the models added by this call. +/// One side of a tree comparison (head or base). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderAddResult +public sealed class PushAttachmentGitHubTreeComparisonSide { - /// Synthesized selectable model entries for the newly added BYOK models, each under its provider-qualified selection id (`provider/id`). Empty when only providers were added. - [JsonPropertyName("models")] - public IList Models { get => field ??= []; set; } + /// Repository the revision belongs to. + [JsonPropertyName("repo")] + public PushGitHubRepoRef Repo { get => field ??= new(); set; } + + /// Git revision (branch, tag, or commit SHA). + [JsonPropertyName("revision")] + public string Revision { get; set; } = string.Empty; } -/// BYOK providers and/or models to add to the session's registry at runtime. Both fields are optional; provide providers, models, or both. +/// Pointer to a comparison between two git revisions. +/// The github_tree_comparison variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ProviderAddRequest +public partial class PushAttachmentGitHubTreeComparison : PushAttachment { - /// BYOK model definitions to register. Each must reference a provider that is already registered or included in this same call. Selection ids (`provider/id`) must be unique across the registry. - [JsonPropertyName("models")] - public IList? Models { get; set; } + /// + [JsonIgnore] + public override string Type => "github_tree_comparison"; - /// Named BYOK provider connections to register, additive to any providers already in the registry. Each name must be unique across the registry and must not contain '/'. - [JsonPropertyName("providers")] - public IList? Providers { get; set; } + /// Base side of the comparison. + [JsonPropertyName("base")] + public required PushAttachmentGitHubTreeComparisonSide Base { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Head side of the comparison. + [JsonPropertyName("head")] + public required PushAttachmentGitHubTreeComparisonSide Head { get; set; } + + /// URL to the comparison on GitHub. + [JsonPropertyName("url")] + public required string Url { get; set; } } -/// The selectable model entries and selection ids synthesized for the synchronized BYOK models. +/// Generic GitHub URL reference. +/// The github_url variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderSyncResult +public partial class PushAttachmentGitHubUrl : PushAttachment { - /// True when synchronization withdrew the selected host-managed model, leaving the session with no explicit selection, so ordinary model resolution picks the session default. Synchronization never promotes a surviving host model in its place: publishing a model offers it, and the choice of which model to use stays with the user. - [JsonPropertyName("modelDeselected")] - public bool? ModelDeselected { get; set; } - - /// Synthesized selectable model entries for the synchronized BYOK models. - [JsonPropertyName("models")] - public IList Models { get => field ??= []; set; } + /// + [JsonIgnore] + public override string Type => "github_url"; - /// Provider-qualified model selection ids present after synchronization. - [JsonPropertyName("selectionIds")] - public IList SelectionIds { get => field ??= []; set; } + /// URL to the GitHub resource. + [JsonPropertyName("url")] + public required string Url { get; set; } } -/// Authoritative BYOK provider and model registry snapshot to apply atomically to the session. +/// Pointer to a file in a GitHub repository at a specific ref. +/// The github_file variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ProviderSyncRequest +public partial class PushAttachmentGitHubFile : PushAttachment { - /// BYOK model definition snapshot. Models absent from this list are removed. - [JsonPropertyName("models")] - public IList? Models { get; set; } + /// + [JsonIgnore] + public override string Type => "github_file"; - /// Named BYOK provider connection snapshot. Providers absent from this list are removed. - [JsonPropertyName("providers")] - public IList? Providers { get; set; } + /// Repository-relative path to the file. + [JsonPropertyName("path")] + public required string Path { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Git ref the file is read at (branch, tag, or commit SHA). + [JsonPropertyName("ref")] + public required string Ref { get; set; } + + /// Repository the file lives in. + [JsonPropertyName("repo")] + public required PushGitHubRepoRef Repo { get; set; } + + /// URL to the file on GitHub. + [JsonPropertyName("url")] + public required string Url { get; set; } } -/// What the withdrawal actually removed from the registry. +/// Pointer to a line range inside a file in a GitHub repository. +/// The github_snippet variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderWithdrawResult +public partial class PushAttachmentGitHubSnippet : PushAttachment { - /// True when withdrawal removed the selected host-managed model, leaving the session with no explicit selection, so ordinary model resolution picks the session default. Withdrawal never promotes a surviving model in its place: the choice of which model to use stays with the user. - [JsonPropertyName("modelDeselected")] - public bool? ModelDeselected { get; set; } + /// + [JsonIgnore] + public override string Type => "github_snippet"; - /// Providers removed because one of the withdrawn models was the last entry referencing them. A provider that merely has no models is not removed. - [JsonPropertyName("providersRemoved")] - public IList ProvidersRemoved { get => field ??= []; set; } + /// Line range the snippet covers. + [JsonPropertyName("lineRange")] + public required PushAttachmentFileLineRange LineRange { get; set; } - /// Selection ids that were registered and are now withdrawn. Excludes requested ids that were not present. - [JsonPropertyName("withdrawn")] - public IList Withdrawn { get => field ??= []; set; } + /// Repository-relative path to the file. + [JsonPropertyName("path")] + public required string Path { get; set; } + + /// Git ref the file is read at (branch, tag, or commit SHA). + [JsonPropertyName("ref")] + public required string Ref { get; set; } + + /// Repository the file lives in. + [JsonPropertyName("repo")] + public required PushGitHubRepoRef Repo { get; set; } + + /// URL to the snippet on GitHub (with line anchor). + [JsonPropertyName("url")] + public required string Url { get; set; } } -/// Host-managed model selection ids to withdraw from the session's BYOK registry. +/// Blob attachment with inline base64-encoded data. +/// The blob variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ProviderWithdrawRequest +public partial class PushAttachmentBlob : PushAttachment { - /// Provider-qualified selection ids to withdraw. Ids that are not registered are ignored, so withdrawal is idempotent. A provider left with no models referencing it is removed too. - [JsonPropertyName("models")] - public IList Models { get => field ??= []; set; } + /// + [JsonIgnore] + public override string Type => "blob"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Base64-encoded content. + [Base64String] + [JsonPropertyName("data")] + public required string Data { get; set; } + + /// User-facing display name for the attachment. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("displayName")] + public string? DisplayName { get; set; } + + /// MIME type of the inline data. + [JsonPropertyName("mimeType")] + public required string MimeType { get; set; } } -/// Indicates whether the session options patch was applied successfully. +/// Slim input shape for extension_context attachments; identity fields are runtime-derived. +/// The extension_context variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionUpdateOptionsResult +public partial class PushAttachmentExtensionContext : PushAttachment { - /// Number of hooks loaded from installed plugins, returned when installedPlugins is updated. - [JsonPropertyName("pluginHookCount")] - public long? PluginHookCount { get; set; } + /// + [JsonIgnore] + public override string Type => "extension_context"; + + /// Caller-supplied JSON payload (required, may be null but not undefined). + [JsonPropertyName("payload")] + public required JsonElement Payload { get; set; } - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Human-readable composer pill label. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("title")] + public required string Title { get; set; } } -/// Source descriptor for a `session.options.update` content-exclusion rule, with source name and type. +/// Parameters for session.extensions.sendAttachmentsToMessage. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class OptionsUpdateAdditionalContentExclusionPolicyRuleSource +internal sealed class SendAttachmentsToMessageParams { - /// Name of the policy source. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Attachments to push into the next user-message turn. extension_context entries take the slim shape; standard variants take their full AttachmentSchema shape. + [JsonPropertyName("attachments")] + public IList Attachments { get => field ??= []; set; } - /// Type of the policy source. - [JsonPropertyName("type")] - public string Type { get; set; } = string.Empty; + /// Optional canvas instance binding the push for provenance. When supplied, the runtime resolves the canvas, verifies it is owned by the calling extension, and stamps canvasId/instanceId onto each extension_context entry. When omitted, no resolution runs and those fields stay unset on the attachment. + [JsonPropertyName("instanceId")] + public string? InstanceId { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Single content-exclusion rule supplied to `session.options.update`, with paths, match conditions, and source. +/// A tool name and arguments to execute through the session's native invocation pipeline. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class OptionsUpdateAdditionalContentExclusionPolicyRule +internal sealed class ToolsExecuteRequest { - /// Conditions of which at least one must match. - [JsonPropertyName("ifAnyMatch")] - public IList? IfAnyMatch { get; set; } + /// Arguments supplied to the tool. + [JsonPropertyName("arguments")] + public JsonElement Arguments { get; set; } - /// Conditions none of which may match. - [JsonPropertyName("ifNoneMatch")] - public IList? IfNoneMatch { get; set; } + /// Name of the currently offered tool to execute. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Path patterns covered by this rule. - [JsonPropertyName("paths")] - public IList Paths { get => field ??= []; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Source descriptor for a `session.options.update` content-exclusion rule, with source name and type. - [JsonPropertyName("source")] - public OptionsUpdateAdditionalContentExclusionPolicyRuleSource Source { get => field ??= new(); set; } + /// Optional identifier used to correlate this invocation with its tool call. + [JsonPropertyName("toolCallId")] + public string? ToolCallId { get; set; } } -/// Content-exclusion policy supplied to `session.options.update`, with rules, last-updated data, and scope. +/// Custom grammar input format accepted by a built-in tool. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class OptionsUpdateAdditionalContentExclusionPolicy +public sealed class BuiltinToolFormat { - /// Opaque policy update timestamp supplied by the host. - [JsonPropertyName("last_updated_at")] - public JsonElement LastUpdatedAt { get; set; } + /// Grammar definition accepted by the tool. + [JsonPropertyName("definition")] + public string Definition { get; set; } = string.Empty; - /// Content-exclusion rules to apply. - [JsonPropertyName("rules")] - public IList Rules { get => field ??= []; set; } + /// Grammar syntax used by the format definition. + [JsonPropertyName("syntax")] + public string Syntax { get; set; } = string.Empty; - /// Allowed values for the `OptionsUpdateAdditionalContentExclusionPolicyScope` enumeration. - [JsonPropertyName("scope")] - public OptionsUpdateAdditionalContentExclusionPolicyScope Scope { get; set; } + /// Custom input-format discriminator. + [JsonPropertyName("type")] + public BuiltinToolFormatType Type { get; set; } } -/// Options scoped to the built-in CAPI (Copilot API) provider. +/// JSON Schema object accepted by a built-in tool. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CapiSessionOptions +public sealed class BuiltinToolInputSchema { - /// Routing preference for sessions whose model is `auto`. On create or cold resume, this establishes the preference sent as `tier` on CAPI `/auto` requests; when omitted on cold resume, the runtime restores the last committed preference. On resident resume, a different value requests a safe switch after resume succeeds and cannot change an in-flight turn. Successful switches are persisted for later cold resume. When no preference is supplied or restored, CAPI default routing is used. `fast` is an integrator-only latency preset, not a first-party GitHub Copilot product preference. - [JsonPropertyName("autoTier")] - public AutoTier? AutoTier { get; set; } - - /// Whether to use WebSocket transport for the CAPI Responses API. Enabled by default when the model advertises `ws:/responses` support; set to `false` to force the HTTP Responses transport in environments where WebSockets are blocked (e.g. behind a proxy). Setting this to `false` is equivalent to the `COPILOT_CLI_DISABLE_WEBSOCKET_RESPONSES` environment variable. - [JsonPropertyName("enableWebSocketResponses")] - public bool? EnableWebSocketResponses { get; set; } + /// Root type of the tool input schema. + [JsonPropertyName("type")] + public BuiltinToolInputSchemaType Type { get; set; } } -/// Installed plugin record for a session, with marketplace, version, install time, enabled state, cache path, and source. +/// Rust-owned metadata and input schema for a built-in tool. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionInstalledPlugin +public sealed class BuiltinToolDescriptor { - /// Path where the plugin is cached locally. - [JsonPropertyName("cache_path")] - public string? CachePath { get; set; } + /// Model-facing description of the tool's behavior. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; - /// Whether the plugin is currently enabled. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } + /// Optional custom input format used instead of a JSON Schema. + [JsonPropertyName("format")] + public BuiltinToolFormat? Format { get; set; } - /// Installation timestamp (ISO-8601). - [JsonPropertyName("installed_at")] - public string InstalledAt { get; set; } = string.Empty; + /// Whether the tool provides a specialized intention summary. + [JsonPropertyName("hasSummariseIntention")] + public bool HasSummariseIntention { get; set; } - /// Absolute path of the marketplace directory a live plugin was resolved from. Present only on live, never-persisted records — those synthesized at session start for a directory/local marketplace, whose cache_path points at the real plugin directory on disk rather than a copy under the installed-plugins cache. Its presence is what marks a record as live, and no record carrying it is ever written to the persisted installedPlugins key. - [JsonPropertyName("installed_from")] - public string? InstalledFrom { get; set; } + /// JSON Schema for the tool input, or null when the tool uses a custom format. + [JsonPropertyName("inputSchema")] + public BuiltinToolInputSchema? InputSchema { get; set; } - /// Marketplace the plugin came from (empty string for direct repo installs). - [JsonPropertyName("marketplace")] - public string Marketplace { get; set; } = string.Empty; + /// Optional supplemental usage instructions for the tool. + [JsonPropertyName("instructions")] + public string? Instructions { get; set; } - /// Plugin name. + /// Whether the tool executes commands in a terminal. + [JsonPropertyName("isTerminal")] + public bool IsTerminal { get; set; } + + /// Stable name used to invoke the built-in tool. [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; - /// Source descriptor for direct repo installs (when marketplace is empty). - [JsonPropertyName("source")] - public JsonElement? Source { get; set; } + /// Policy describing which tool metadata may be recorded without obfuscation. + [JsonPropertyName("safeForTelemetry")] + public JsonElement SafeForTelemetry { get; set; } - /// Per-plugin source fingerprint (a SHA-256 hash of the plugin's catalog source spec plus its resolved source subtree — NOT a Git commit SHA) captured at marketplace install/update time. Auto-update compares it against the freshly recomputed fingerprint to detect a content change that does not bump the version. Absent for pre-existing installs and for direct (non-marketplace) installs. - [JsonPropertyName("source_sha")] - public string? SourceSha { get; set; } + /// Optional human-readable title for the tool. + [JsonPropertyName("title")] + public string? Title { get; set; } - /// Installed version, if known. - [JsonPropertyName("version")] - public string? Version { get; set; } + /// Optional tool category discriminator. + [JsonPropertyName("type")] + public string? Type { get; set; } } -/// Custom model-provider configuration (BYOK). +/// Rust-owned built-in tool descriptors for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderConfig +public sealed class ToolsGetBuiltinDescriptorsResult { - /// API key. Optional for local providers like Ollama. - [JsonPropertyName("apiKey")] - public string? ApiKey { get; set; } - - /// Azure-specific provider options. - [JsonPropertyName("azure")] - public ProviderConfigAzure? Azure { get; set; } - - /// API endpoint URL. - [JsonPropertyName("baseUrl")] - public string BaseUrl { get; set; } = string.Empty; - - /// Bearer token for authentication. Sets the Authorization header directly. Takes precedence over apiKey when both are set. - [JsonPropertyName("bearerToken")] - public string? BearerToken { get; set; } + /// Built-in tool descriptors materialized for the session. + [JsonPropertyName("tools")] + public IList Tools { get => field ??= []; set; } +} - /// When true, the SDK client supplies bearer tokens on demand: the runtime calls the client-session `providerToken.getToken` callback before each request and applies the returned token as an `Authorization: Bearer <token>` header. This is the bearer/OAuth scheme used by Azure AD / managed-identity tokens and provider OAuth access tokens (including Anthropic's), not a provider-specific API-key header such as Anthropic's `x-api-key`. The token-acquiring function itself stays on the SDK side and is never serialized; only this flag crosses the wire. When set alongside `apiKey`/`bearerToken`, the callback takes precedence: the runtime applies the token returned by `providerToken.getToken` as the `Authorization: Bearer` header for each request and does not send the static credential. - [JsonPropertyName("hasBearerTokenProvider")] - public bool? HasBearerTokenProvider { get; set; } +/// Shell-specific names and description lines used to materialize built-in shell tool descriptors. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ToolsShellDescriptorConfig +{ + /// Additional model-facing shell description lines. + [JsonPropertyName("descriptionLines")] + public IList DescriptionLines { get => field ??= []; set; } - /// Custom HTTP headers to include in all outbound requests to the provider. - [JsonPropertyName("headers")] - public IDictionary? Headers { get; set; } + /// Human-readable shell name. + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } = string.Empty; - /// Maximum context window tokens for the model. - [JsonPropertyName("maxContextWindowTokens")] - public double? MaxContextWindowTokens { get; set; } + /// Tool name used to list active shells. + [JsonPropertyName("listShellsToolName")] + public string ListShellsToolName { get; set; } = string.Empty; - /// Maximum output tokens for the model. - [JsonPropertyName("maxOutputTokens")] - public double? MaxOutputTokens { get; set; } + /// Tool name used to read shell output. + [JsonPropertyName("readShellToolName")] + public string ReadShellToolName { get; set; } = string.Empty; - /// Maximum prompt/input tokens for the model. - [JsonPropertyName("maxPromptTokens")] - public double? MaxPromptTokens { get; set; } + /// Tool name used to start shell commands. + [JsonPropertyName("shellToolName")] + public string ShellToolName { get; set; } = string.Empty; - /// Overrides for model capabilities when they cannot be inferred from modelId. - [JsonPropertyName("modelCapabilities")] - public ModelCapabilitiesOverride? ModelCapabilities { get; set; } + /// Stable shell type identifier. + [JsonPropertyName("shellType")] + public string ShellType { get; set; } = string.Empty; - /// Well-known model ID used for capability lookup. When set, agent behavior config and token limits are inferred from this model. - [JsonPropertyName("modelId")] - public string? ModelId { get; set; } + /// Tool name used to stop shell commands. + [JsonPropertyName("stopShellToolName")] + public string StopShellToolName { get; set; } = string.Empty; +} - /// The product serving the model, reported in telemetry as `model_provider`. Set it when `type` alone cannot identify the product, such as Ollama or LM Studio behind an OpenAI-compatible endpoint. Only affects telemetry. - [JsonPropertyName("modelProvider")] - public ProviderConfigModelProvider? ModelProvider { get; set; } +/// Options controlling how Rust-owned built-in tool descriptors are materialized. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ToolsGetBuiltinDescriptorsRequest +{ + /// Whether background task completion notifications are enabled. + [JsonPropertyName("backgroundTaskNotificationsEnabled")] + public bool? BackgroundTaskNotificationsEnabled { get; set; } - /// Provider name used for model and telemetry attribution. - [JsonPropertyName("providerName")] - public string? ProviderName { get; set; } + /// Whether tool descriptors should include authoring metadata. + [JsonPropertyName("includeAuthor")] + public bool? IncludeAuthor { get; set; } - /// Provider transport. Defaults to "http". - [JsonPropertyName("transport")] - public ProviderConfigTransport? Transport { get; set; } + /// Whether descriptors should favor fewer user-intervention prompts. + [JsonPropertyName("reduceUserIntervention")] + public bool? ReduceUserIntervention { get; set; } - /// Provider type. Defaults to "openai" for generic OpenAI-compatible APIs. - [JsonPropertyName("type")] - public ProviderConfigType? Type { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Wire API format (openai/azure only). Defaults to "completions". - [JsonPropertyName("wireApi")] - public ProviderConfigWireApi? WireApi { get; set; } + /// Shell-specific names and description lines for shell tools. + [JsonPropertyName("shellConfig")] + public ToolsShellDescriptorConfig? ShellConfig { get; set; } - /// The model identifier sent to the provider API for inference (the "wire" model), as opposed to modelId which is the well-known base. - [JsonPropertyName("wireModel")] - public string? WireModel { get; set; } -} + /// Whether the configured shell supports PowerShell 7 syntax. + [JsonPropertyName("shellSupportsPowerShell7Syntax")] + public bool? ShellSupportsPowerShell7Syntax { get; set; } -/// -/// Command-scoped GitHub credential injection for the shell commands an agent runs. -/// -/// Each channel is opt-in and independent, and injection is scoped to the individual command -/// spawn: the credential is resolved from the session's *current* authentication at every spawn -/// and reaches only spawns whose script actually invokes `git` or `gh`. Because nothing is -/// retained between spawns, replacing the session credential (`session.gitHubAuth.setCredentials`) -/// changes what the next spawned command presents — which seeding a credential into the runtime -/// process's own environment cannot do, since a child's environment is fixed at `exec`. -/// -/// The credential is matched to the host it authenticates to, so a github.com credential is never -/// presented to a GitHub Enterprise host and vice versa. Where a channel cannot express that -/// boundary it injects nothing rather than crossing it -- see `gh` below. -/// -/// This is independent of `sandboxConfig`: it is a decision about which identity the agent -/// presents, not about what the agent may touch, and it works on every platform whether or not -/// an OS sandboxing backend is available. `sandboxConfig.auth` remains the sandbox-scoped -/// spelling and is additive with this one. -/// -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ShellCredentials -{ - /// - /// Whether to authenticate the agent's `gh` commands as the session's GitHub credential, by - /// exporting `GH_TOKEN` to a spawn that runs `gh`. Any inherited `gh` credential is removed from - /// spawns that do not, so the credential stays command-scoped. - /// - /// Applies to a github.com credential only. `gh` picks its credential variable from the host a - /// command targets rather than the one the credential belongs to, and the command can choose that - /// target, so `GH_ENTERPRISE_TOKEN` would offer a single-tenant enterprise credential to every - /// other enterprise host. A session whose credential is enterprise-scoped therefore runs `gh` - /// unauthenticated; its `git` commands are unaffected, because `http.<host>.extraheader` is scoped - /// to one host by construction. Default: false (opt-in). - /// - [JsonPropertyName("gh")] - public bool? Gh { get; set; } + /// Default shell timeout in milliseconds. + [JsonPropertyName("shellTimeoutMs")] + public double? ShellTimeoutMs { get; set; } - /// - /// Whether to authenticate the agent's `git` commands as the session's GitHub credential, by - /// injecting an `http.<host>.extraheader` (plus `insteadOf` rewrites so SSH-spelled remotes for - /// that host use the authenticated HTTPS transport). Applied only to a spawn that runs a - /// remote-contacting `git` subcommand. Default: false (opt-in). - /// - [JsonPropertyName("git")] - public bool? Git { get; set; } + /// Whether semantic skill lookup is available. + [JsonPropertyName("skillEmbeddingEnabled")] + public bool? SkillEmbeddingEnabled { get; set; } } -/// A host-provided script sourced before each built-in shell command when its shell target matches the active shell. +/// Task completion notification with summary from the agent. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ShellInitScript +public sealed class TaskCompleteData { - /// Path to the script to source. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Structured blocker details when outcome is blocked. + [JsonPropertyName("blocker")] + public TaskBlocker? Blocker { get; set; } - /// Built-in shell that may source this script. - [JsonPropertyName("shell")] - public ShellInitScriptShell Shell { get; set; } -} + /// Active autopilot objective ID evaluated by the completion reviewer. + [JsonPropertyName("objectiveId")] + public long? ObjectiveId { get; set; } -/// Per-session settings for built-in shell tools. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ShellOptions -{ - /// Command-scoped GitHub credential injection for shell commands. - [JsonPropertyName("credentials")] - public ShellCredentials? Credentials { get; set; } + /// Semantic completion decision. Absent on legacy events and invalid tool calls. + [JsonPropertyName("outcome")] + public TaskCompletionOutcome? Outcome { get; set; } - /// Controls automatic non-interactive profile loading where supported. Explicit initScripts are unaffected. - [JsonPropertyName("initProfile")] - public ShellInitProfile? InitProfile { get; set; } + /// Label-safe runtime rationale for the completion decision (e.g. a cancellation or pause/resume downgrade), when one applies. Reviewer-authored rationale is intentionally omitted here because this event has no IFC label channel; the reviewer's findings remain available through its own labeled sub-agent events. + [JsonPropertyName("reason")] + public string? Reason { get; set; } - /// - /// Ordered host-provided script paths sourced before each built-in shell command when the - /// entry's shell target matches the active shell. Use these for rc files, environment setup scripts, - /// or other custom scripts. A script that returns a nonzero status is reported, and later scripts - /// and the user command continue while the shell remains running. Because scripts are sourced into - /// the command shell, `exit`, `exec`, failures under `set -e`, or other shell-terminating behavior - /// can prevent continuation. Script standard output is preserved; Bash script stderr is discarded, - /// PowerShell exception messages are replaced, and runtime-generated failure notices omit - /// configured script paths. When sandboxing is enabled, each script must already be readable under - /// the active sandbox filesystem policy. Pass an empty array to clear the list. - /// - [JsonPropertyName("initScripts")] - public IList? InitScripts { get; set; } + /// Whether the task was accepted as complete. False when validation failed or completion was rejected or blocked by the reviewer. + [JsonPropertyName("success")] + public bool? Success { get; set; } - /// - /// Flags passed to the active built-in shell process on startup, replacing its default flags. - /// When omitted, the built-in Bash shell uses `--norc --noprofile`, - /// and the built-in PowerShell shell uses `-NoProfile -NoLogo`. - /// - [JsonPropertyName("processFlags")] - public IList? ProcessFlags { get; set; } + /// Summary of the completed task, provided by the agent. + [JsonPropertyName("summary")] + public string? Summary { get; set; } } -/// Patch of mutable session options to apply to the running session. +/// Binary result returned by a tool for the model. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionUpdateOptionsParams +public sealed class ExternalToolTextResultForLlmBinaryResultsForLlm { - /// Additional content-exclusion policies to merge into the session's policy set. - [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] - [JsonPropertyName("additionalContentExclusionPolicies")] - public IList? AdditionalContentExclusionPolicies { get; set; } - - /// Runtime context discriminator (e.g., `cli`, `actions`). - [JsonPropertyName("agentContext")] - public string? AgentContext { get; set; } - - /// Whether to include instructions from every MCP server in the system prompt instead of only allowlisted servers. - [JsonPropertyName("allowAllMcpServerInstructions")] - public bool? AllowAllMcpServerInstructions { get; set; } - - /// Whether to disable the `ask_user` tool (encourages autonomous behavior). - [JsonPropertyName("askUserDisabled")] - public bool? AskUserDisabled { get; set; } - - /// Allowlist of tool names available to this session. - [JsonPropertyName("availableTools")] - public IList? AvailableTools { get; set; } + /// Base64-encoded binary data. + [Base64String] + [JsonPropertyName("data")] + public string Data { get; set; } = string.Empty; - /// Options scoped to the built-in CAPI (Copilot API) provider. - [JsonPropertyName("capi")] - public CapiSessionOptions? Capi { get; set; } + /// Human-readable description of the binary data. + [JsonPropertyName("description")] + public string? Description { get; set; } - /// Identifier of the client driving the session. - [JsonPropertyName("clientName")] - public string? ClientName { get; set; } + /// Optional metadata from the producing tool. + [JsonPropertyName("metadata")] + public IDictionary? Metadata { get; set; } - /// Whether to include the `Co-authored-by` trailer in commit messages. - [JsonPropertyName("coauthorEnabled")] - public bool? CoauthorEnabled { get; set; } + /// MIME type of the binary data. + [JsonPropertyName("mimeType")] + public string MimeType { get; set; } = string.Empty; - /// Context tier for models with tiered pricing. The session uses this to derive effective `modelCapabilitiesOverrides` so compaction, truncation, token display, and request limits honor the selected tier. - [JsonPropertyName("contextTier")] - public OptionsUpdateContextTier? ContextTier { get; set; } + /// Binary result type discriminator. Use "image" for images and "resource" for other binary data. + [JsonPropertyName("type")] + public ExternalToolTextResultForLlmBinaryResultsForLlmType Type { get; set; } +} - /// Whether to allow auto-mode continuation across turns. - [JsonPropertyName("continueOnAutoMode")] - public bool? ContinueOnAutoMode { get; set; } +/// A content block within a tool result, which may be text, terminal output, image, audio, or a resource. +/// Polymorphic base type discriminated by type. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "type", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(ExternalToolTextResultForLlmContentText), "text")] +[JsonDerivedType(typeof(ExternalToolTextResultForLlmContentTerminal), "terminal")] +[JsonDerivedType(typeof(ExternalToolTextResultForLlmContentShellExit), "shell_exit")] +[JsonDerivedType(typeof(ExternalToolTextResultForLlmContentImage), "image")] +[JsonDerivedType(typeof(ExternalToolTextResultForLlmContentAudio), "audio")] +[JsonDerivedType(typeof(ExternalToolTextResultForLlmContentResourceLink), "resource_link")] +[JsonDerivedType(typeof(ExternalToolTextResultForLlmContentResource), "resource")] +public partial class ExternalToolTextResultForLlmContent +{ + /// The type discriminator. + [JsonPropertyName("type")] + public virtual string Type { get; set; } = string.Empty; +} - /// Override URL for the Copilot API endpoint. - [JsonPropertyName("copilotUrl")] - public string? CopilotUrl { get; set; } - /// Whether to default custom agents to local-only execution. - [JsonPropertyName("customAgentsLocalOnly")] - public bool? CustomAgentsLocalOnly { get; set; } +/// Plain text content block. +/// The text variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class ExternalToolTextResultForLlmContentText : ExternalToolTextResultForLlmContent +{ + /// + [JsonIgnore] + public override string Type => "text"; - /// Instruction source IDs to exclude from the system prompt. - [JsonPropertyName("disabledInstructionSources")] - public IList? DisabledInstructionSources { get; set; } + /// The text content. + [JsonPropertyName("text")] + public required string Text { get; set; } +} - /// Skill IDs that should be excluded from this session. - [JsonPropertyName("disabledSkills")] - public IList? DisabledSkills { get; set; } +/// Terminal/shell output content block with optional exit code and working directory. +/// The terminal variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class ExternalToolTextResultForLlmContentTerminal : ExternalToolTextResultForLlmContent +{ + /// + [JsonIgnore] + public override string Type => "terminal"; - /// Whether to enable loading of `.github/hooks/` filesystem hooks. Separate from the SDK callback hook mechanism. - [JsonPropertyName("enableFileHooks")] - public bool? EnableFileHooks { get; set; } + /// Working directory where the command was executed. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("cwd")] + public string? Cwd { get; set; } - /// Whether to enable host git operations (context resolution, child repo scanning, git info in system prompt). - [JsonPropertyName("enableHostGitOperations")] - public bool? EnableHostGitOperations { get; set; } + /// Process exit code, if the command has completed. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("exitCode")] + public long? ExitCode { get; set; } - /// Whether to discover custom instructions on demand after successful file views (AGENTS.md / CLAUDE.md / .github/copilot-instructions.md surfacing). Combined with `skipCustomInstructions`. - [JsonPropertyName("enableOnDemandInstructionDiscovery")] - public bool? EnableOnDemandInstructionDiscovery { get; set; } + /// Terminal/shell output text. + [JsonPropertyName("text")] + public required string Text { get; set; } +} - /// Whether to surface reasoning-summary events from the model. - [JsonPropertyName("enableReasoningSummaries")] - public bool? EnableReasoningSummaries { get; set; } +/// Shell command exit metadata with optional output preview. +/// The shell_exit variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class ExternalToolTextResultForLlmContentShellExit : ExternalToolTextResultForLlmContent +{ + /// + [JsonIgnore] + public override string Type => "shell_exit"; - /// Whether shell-script safety heuristics are enabled. - [JsonPropertyName("enableScriptSafety")] - public bool? EnableScriptSafety { get; set; } + /// Working directory where the shell command was executed. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("cwd")] + public string? Cwd { get; set; } - /// Whether to enable cross-session store writes and reads. - [JsonPropertyName("enableSessionStore")] - public bool? EnableSessionStore { get; set; } + /// Exit code from the completed shell command. + [JsonPropertyName("exitCode")] + public required long ExitCode { get; set; } - /// Whether skill loading is enabled. Explicit false disables every source, including a bound SDK provider; changing the value invalidates the loaded skill snapshot. When omitted, creation falls back to enableConfigDiscovery unless an SDK skill provider is registered. - [JsonPropertyName("enableSkills")] - public bool? EnableSkills { get; set; } + /// Path reported in the shell session's filesystem namespace when shell output exceeded the configured large-output threshold. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("outputFilePath")] + public string? OutputFilePath { get; set; } - /// Whether to stream model responses. - [JsonPropertyName("enableStreaming")] - public bool? EnableStreaming { get; set; } + /// Output associated with this shell command, if available. May be partial, truncated, or a preview; not guaranteed to be full output. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("outputPreview")] + public string? OutputPreview { get; set; } - /// How env values are passed to MCP servers (`direct` inlines literal values; `indirect` resolves at launch). - [JsonPropertyName("envValueMode")] - public OptionsUpdateEnvValueMode? EnvValueMode { get; set; } + /// Whether outputPreview is known to be incomplete or truncated. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("outputTruncated")] + public bool? OutputTruncated { get; set; } - /// Override directory for the session-events log. When unset, the runtime's default events log directory is used. - [JsonPropertyName("eventsLogDirectory")] - public string? EventsLogDirectory { get; set; } + /// Shell id, as assigned by Copilot runtime. + [JsonPropertyName("shellId")] + public required string ShellId { get; set; } +} - /// Whether subagent callback events should be forwarded into the session event log sink. - [JsonPropertyName("eventsLogIncludesSubagents")] - public bool? EventsLogIncludesSubagents { get; set; } +/// Image content block with base64-encoded data. +/// The image variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class ExternalToolTextResultForLlmContentImage : ExternalToolTextResultForLlmContent +{ + /// + [JsonIgnore] + public override string Type => "image"; - /// Built-in subagent names to exclude from this session. Excluded built-ins are hidden from agent discovery and cannot be dispatched unless a custom agent with the same name is available. - [JsonPropertyName("excludedBuiltinAgents")] - public IList? ExcludedBuiltinAgents { get; set; } + /// Base64-encoded image data. + [Base64String] + [JsonPropertyName("data")] + public required string Data { get; set; } - /// Denylist of tool names for this session. - [JsonPropertyName("excludedTools")] - public IList? ExcludedTools { get; set; } + /// MIME type of the image (e.g., image/png, image/jpeg). + [JsonPropertyName("mimeType")] + public required string MimeType { get; set; } +} - /// Map of feature-flag IDs to their boolean enabled state. - [JsonPropertyName("featureFlags")] - public IDictionary? FeatureFlags { get; set; } +/// Audio content block with base64-encoded data. +/// The audio variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class ExternalToolTextResultForLlmContentAudio : ExternalToolTextResultForLlmContent +{ + /// + [JsonIgnore] + public override string Type => "audio"; - /// Skill scan directories and descendants excluded from discovery. Supports `~`-relative paths. - [JsonPropertyName("ignoredSkillsLocations")] - public IList? IgnoredSkillsLocations { get; set; } + /// Base64-encoded audio data. + [Base64String] + [JsonPropertyName("data")] + public required string Data { get; set; } - /// Built-in subagent names to include in this session. When specified, only these built-ins are available, subject to runtime availability and exclusions. Custom agents with the same name remain available. Set to null to remove the allowlist restriction. - [JsonPropertyName("includedBuiltinAgents")] - public IList? IncludedBuiltinAgents { get; set; } + /// MIME type of the audio (e.g., audio/wav, audio/mpeg). + [JsonPropertyName("mimeType")] + public required string MimeType { get; set; } +} - /// Built-in skill names to include in this session. When specified, only these runtime-bundled skills are available. Skills from other sources with the same name remain available. Set to null to remove the allowlist restriction. - [JsonPropertyName("includedBuiltinSkills")] - public IList? IncludedBuiltinSkills { get; set; } +/// Icon image for a resource. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ExternalToolTextResultForLlmContentResourceLinkIcon +{ + /// MIME type of the icon image. + [JsonPropertyName("mimeType")] + public string? MimeType { get; set; } - /// Full set of installed plugins for the session. Replaces the existing list; the runtime invalidates the skills cache only when the list materially changes. - [JsonPropertyName("installedPlugins")] - public IList? InstalledPlugins { get; set; } + /// Available icon sizes (e.g., ['16x16', '32x32']). + [JsonPropertyName("sizes")] + public IList? Sizes { get; set; } - /// Stable integration identifier used for analytics and rate-limit attribution. - [JsonPropertyName("integrationId")] - public string? IntegrationId { get; set; } + /// URL or path to the icon image. + [JsonPropertyName("src")] + public string Src { get; set; } = string.Empty; - /// Whether experimental capabilities are enabled. - [JsonPropertyName("isExperimentalMode")] - public bool? IsExperimentalMode { get; set; } + /// Theme variant this icon is intended for. + [JsonPropertyName("theme")] + public ExternalToolTextResultForLlmContentResourceLinkIconTheme? Theme { get; set; } +} - /// Whether interactive shell sessions are logged. - [JsonPropertyName("logInteractiveShells")] - public bool? LogInteractiveShells { get; set; } +/// Resource link content block referencing an external resource. +/// The resource_link variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class ExternalToolTextResultForLlmContentResourceLink : ExternalToolTextResultForLlmContent +{ + /// + [JsonIgnore] + public override string Type => "resource_link"; - /// Identifier sent to LSP-style integrations. - [JsonPropertyName("lspClientName")] - public string? LspClientName { get; set; } + /// Human-readable description of the resource. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("description")] + public string? Description { get; set; } - /// Whether to expose the `manage_schedule` tool to the agent. The runtime always owns the per-session schedule registry; this flag only controls tool exposure (typically gated to staff users). - [JsonPropertyName("manageScheduleEnabled")] - public bool? ManageScheduleEnabled { get; set; } + /// Icons associated with this resource. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("icons")] + public IList? Icons { get; set; } - /// Maximum decoded byte size of a single model-facing binary tool result (e.g. an image) persisted inline in session events and re-presented to the model on later turns / resume. Larger results are persisted as a metadata-only marker and shown to the model as a short text note. Defaults to 10 MB. - [JsonPropertyName("maxInlineBinaryBytes")] - public long? MaxInlineBinaryBytes { get; set; } + /// MIME type of the resource content. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("mimeType")] + public string? MimeType { get; set; } - /// The model ID to use for assistant turns. - [JsonPropertyName("model")] - public string? Model { get; set; } + /// Resource name identifier. + [JsonPropertyName("name")] + public required string Name { get; set; } - /// Per-property model capability overrides for the selected model. - [JsonPropertyName("modelCapabilitiesOverrides")] - public ModelCapabilitiesOverride? ModelCapabilitiesOverrides { get; set; } + /// Size of the resource in bytes. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("size")] + public long? Size { get; set; } - /// Organization-level custom instructions to inject into the system prompt. - [JsonPropertyName("organizationCustomInstructions")] - public string? OrganizationCustomInstructions { get; set; } + /// Human-readable display title for the resource. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("title")] + public string? Title { get; set; } - /// Custom model-provider configuration (BYOK). - [JsonPropertyName("provider")] - public ProviderConfig? Provider { get; set; } + /// URI identifying the resource. + [JsonPropertyName("uri")] + public required string Uri { get; set; } +} - /// Reasoning effort for the selected model. CAPI values are model-defined and validated against the selected model; BYOK providers may define additional values. When omitted, no effort override is applied. - [JsonPropertyName("reasoningEffort")] - public string? ReasoningEffort { get; set; } +/// Embedded resource content block with inline text or binary data. +/// The resource variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class ExternalToolTextResultForLlmContentResource : ExternalToolTextResultForLlmContent +{ + /// + [JsonIgnore] + public override string Type => "resource"; - /// Reasoning summary mode for supported model clients. - [JsonPropertyName("reasoningSummary")] - public OptionsUpdateReasoningSummary? ReasoningSummary { get; set; } + /// The embedded resource contents, either text or base64-encoded binary. + [JsonPropertyName("resource")] + public required JsonElement Resource { get; set; } +} - /// Whether the session is running in an interactive UI. - [JsonPropertyName("runningInInteractiveMode")] - public bool? RunningInInteractiveMode { get; set; } +/// A message injected by a tool result. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ToolResultNewMessage +{ + /// Message content to inject after the tool result. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; - /// Resolved sandbox configuration. - [JsonPropertyName("sandboxConfig")] - public SandboxConfig? SandboxConfig { get; set; } + /// Source attributed to the injected message. + [JsonPropertyName("source")] + public string Source { get; set; } = string.Empty; +} - /// Origin of the sandbox choice. Settings-derived origins (never_configured, user_enabled, user_disabled, repository_policy) let managed policy floor a host preference; explicit below-floor changes remain policy conflicts unless a session opt-out is authorized. Also used for telemetry provenance. - [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] - [JsonPropertyName("sandboxConfigSource")] - public SandboxConfigSource? SandboxConfigSource { get; set; } +/// RPC data type for TaskCompletionDecision operations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class TaskCompletionDecision +{ + /// Objective eligibility token captured when the decision was evaluated. + [JsonPropertyName("completionEligibilityToken")] + public long? CompletionEligibilityToken { get; set; } - /// Replaces the session's capability set with the given list. Use to enable or disable capabilities mid-session (e.g., remove `memory` for reproducible scripted runs). Omit the field to leave the existing capability set unchanged. - [JsonPropertyName("sessionCapabilities")] - public IList? SessionCapabilities { get; set; } + /// Whether completion was accepted after the reviewer-rejection budget was exhausted. + [JsonPropertyName("completionRejectionBudgetExhausted")] + public bool? CompletionRejectionBudgetExhausted { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Active autopilot objective evaluated by the completion reviewer. + [JsonPropertyName("objectiveId")] + public long? ObjectiveId { get; set; } - /// Optional session limits. Pass null to clear the session limits. - [JsonPropertyName("sessionLimits")] - public SessionLimitsConfig? SessionLimits { get; set; } + /// Semantic result of evaluating the task completion request. + [JsonPropertyName("outcome")] + public TaskCompletionOutcome Outcome { get; set; } - /// Per-session settings for built-in shell tools. - [JsonPropertyName("shell")] - public ShellOptions? Shell { get; set; } + /// Rationale for the completion decision, when one is available. + [JsonPropertyName("reason")] + public string? Reason { get; set; } - /// Use shell.initProfile instead. Shell init profile (`None` or `NonInteractive`). - [EditorBrowsable(EditorBrowsableState.Never)] -#if NET5_0_OR_GREATER - [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] -#else - [Obsolete("This member is deprecated and will be removed in a future version.")] -#endif - [JsonPropertyName("shellInitProfile")] - public string? ShellInitProfile { get; set; } + /// Whether the rationale was derived from completion-reviewer output. + [JsonPropertyName("reviewerDerived")] + public bool? ReviewerDerived { get; set; } - /// PowerShell process flags applied to built-in and user-requested shell commands. - [JsonPropertyName("shellProcessFlags")] - public IList? ShellProcessFlags { get; set; } + /// Information-flow metadata captured from the completion reviewer. + [JsonPropertyName("reviewerResultMeta")] + public JsonElement? ReviewerResultMeta { get; set; } +} - /// Additional directories to search for skills. - [JsonPropertyName("skillDirectories")] - public IList? SkillDirectories { get; set; } +/// Expanded canonical result returned by a session tool. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ToolResultExpanded +{ + /// Base64-encoded binary results returned to the model. + [JsonPropertyName("binaryResultsForLlm")] + public IList? BinaryResultsForLlm { get; set; } - /// Whether to skip loading custom instruction sources. - [JsonPropertyName("skipCustomInstructions")] - public bool? SkipCustomInstructions { get; set; } + /// Sources returned by the tool that the model may cite. + [JsonPropertyName("citableSources")] + public IList? CitableSources { get; set; } - /// Whether to skip embedding retrieval pipeline initialization and execution. - [JsonPropertyName("skipEmbeddingRetrieval")] - public bool? SkipEmbeddingRetrieval { get; set; } + /// Structured content blocks returned to the model. + [JsonPropertyName("contents")] + public IList? Contents { get; set; } - /// When true, the selected custom agent's prompt is not injected into the user message (skill context is still injected). Used by automation triggers where the agent prompt is already in the problem statement. - [JsonPropertyName("suppressCustomAgentPrompt")] - public bool? SuppressCustomAgentPrompt { get; set; } + /// Error message for an unsuccessful execution. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Controls how availableTools (allowlist) and excludedTools (denylist) combine when both are set. - [JsonPropertyName("toolFilterPrecedence")] - public OptionsUpdateToolFilterPrecedence? ToolFilterPrecedence { get; set; } + /// Metadata propagated with the tool result, including information-flow labels. + [JsonPropertyName("mcpMeta")] + public IDictionary? McpMeta { get; set; } - /// Optional path for trajectory output. - [JsonPropertyName("trajectoryFile")] - public string? TrajectoryFile { get; set; } + /// Messages to inject after the tool result. + [JsonPropertyName("newMessages")] + public IList? NewMessages { get; set; } - /// Output verbosity level for supported models. - [JsonPropertyName("verbosity")] - public Verbosity? Verbosity { get; set; } + /// Whether post-tool-use failure hooks have already processed this result. + [JsonPropertyName("postToolUseFailureHooksProcessed")] + public bool? PostToolUseFailureHooksProcessed { get; set; } - /// Absolute working-directory path for shell tools. - [JsonPropertyName("workingDirectory")] - public string? WorkingDirectory { get; set; } -} + /// Execution outcome classification. + [JsonPropertyName("resultType")] + public ToolResultType ResultType { get; set; } -/// Parameters for (re)loading the merged LSP configuration set. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class LspInitializeRequest -{ - /// Force re-initialization even when LSP configs were already loaded for the working directory. - [JsonPropertyName("force")] - public bool? Force { get; set; } + /// Detailed log content available for session display. + [JsonPropertyName("sessionLog")] + public string? SessionLog { get; set; } - /// Git root used as the boundary when traversing for project-level LSP configs (supports monorepos). - [JsonPropertyName("gitRoot")] - public string? GitRoot { get; set; } + /// Skill invocation metadata produced by the tool. + [JsonPropertyName("skillInvocation")] + public JsonElement? SkillInvocation { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether large-output post-processing should be skipped. + [JsonPropertyName("skipLargeOutputProcessing")] + public bool? SkipLargeOutputProcessing { get; set; } - /// Working directory used to load project-level LSP configs. Defaults to the session working directory when omitted. - [JsonPropertyName("workingDirectory")] - public string? WorkingDirectory { get; set; } -} + /// Structured result content in addition to the model-facing text. + [JsonPropertyName("structuredContent")] + public JsonElement? StructuredContent { get; set; } -/// Discovered extension metadata, including source-qualified ID, name, discovery source, status, and optional process ID. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class Extension -{ - /// Source-qualified ID (e.g., 'project:my-ext', 'user:auth-helper', 'plugin:my-plugin:my-ext'). - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Completion-review decision produced by the task-completion tool. + [JsonPropertyName("taskCompletionDecision")] + public TaskCompletionDecision? TaskCompletionDecision { get; set; } - /// Extension name (directory name). - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Text result returned to the model. + [JsonPropertyName("textResultForLlm")] + public string TextResultForLlm { get; set; } = string.Empty; - /// Process ID if the extension is running. - [JsonPropertyName("pid")] - public long? Pid { get; set; } + /// Deferred tool names made available by this result. + [JsonPropertyName("toolReferences")] + public IList? ToolReferences { get; set; } - /// Discovery source: project (.github/extensions/), user (~/.copilot/extensions/), plugin (installed plugin), or session (session-state/<id>/extensions/). - [JsonPropertyName("source")] - public ExtensionSource Source { get; set; } + /// Tool-specific telemetry payload. + [JsonPropertyName("toolTelemetry")] + public JsonElement? ToolTelemetry { get; set; } - /// Current status: running, disabled, failed, or starting. - [JsonPropertyName("status")] - public ExtensionStatus Status { get; set; } + /// Optional UI resource produced by the tool. + [JsonPropertyName("uiResource")] + public JsonElement? UiResource { get; set; } } -/// Extensions discovered for the session, with their current status. +/// Task-completion tool arguments and final result used to build a label-safe session event payload. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ExtensionList +internal sealed class ToolsTaskCompleteEventDataRequest { - /// Discovered extensions and their current status. - [JsonPropertyName("extensions")] - public IList Extensions { get => field ??= []; set; } -} + /// Final expanded result returned by the task_complete tool. + [JsonPropertyName("finalResult")] + public ToolResultExpanded FinalResult { get => field ??= new(); set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionExtensionsListRequest -{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Arguments supplied to the completed task_complete tool call. + [JsonPropertyName("toolArgs")] + public JsonElement ToolArgs { get; set; } } -/// Source-qualified extension identifier to enable for the session. +/// Indicates whether the external tool call result was handled successfully. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ExtensionsEnableRequest +public sealed class HandlePendingToolCallResult { - /// Source-qualified extension ID to enable. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether the tool call result was handled successfully. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Source-qualified extension identifier to disable for the session. +/// Pending external tool call request ID, with the tool result or an error describing why it failed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ExtensionsDisableRequest +internal sealed class HandlePendingToolCallRequest { - /// Source-qualified extension ID to disable. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Error message if the tool call failed. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Request ID of the pending tool call. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionExtensionsReloadRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Tool call result (string or expanded result object). + [JsonPropertyName("result")] + public JsonElement? Result { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionExtensionsReconcileRequest -{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Attachment union accepted by push input, covering files, directories, GitHub objects, blobs, snippets, and extension context. -/// Polymorphic base type discriminated by type. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "type", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(PushAttachmentFile), "file")] -[JsonDerivedType(typeof(PushAttachmentDirectory), "directory")] -[JsonDerivedType(typeof(PushAttachmentSelection), "selection")] -[JsonDerivedType(typeof(PushAttachmentGitHubReference), "github_reference")] -[JsonDerivedType(typeof(PushAttachmentGitHubCommit), "github_commit")] -[JsonDerivedType(typeof(PushAttachmentGitHubRelease), "github_release")] -[JsonDerivedType(typeof(PushAttachmentGitHubActionsJob), "github_actions_job")] -[JsonDerivedType(typeof(PushAttachmentGitHubRepository), "github_repository")] -[JsonDerivedType(typeof(PushAttachmentGitHubFileDiff), "github_file_diff")] -[JsonDerivedType(typeof(PushAttachmentGitHubTreeComparison), "github_tree_comparison")] -[JsonDerivedType(typeof(PushAttachmentGitHubUrl), "github_url")] -[JsonDerivedType(typeof(PushAttachmentGitHubFile), "github_file")] -[JsonDerivedType(typeof(PushAttachmentGitHubSnippet), "github_snippet")] -[JsonDerivedType(typeof(PushAttachmentBlob), "blob")] -[JsonDerivedType(typeof(PushAttachmentExtensionContext), "extension_context")] -public partial class PushAttachment -{ - /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { get; set; } = string.Empty; -} - - -/// Optional line range to scope the attachment to a specific section of the file. +/// External tool call of the session or one of its sub-agents that is still waiting for session.tools.handlePendingToolCall. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PushAttachmentFileLineRange +public sealed class PendingExternalToolRequest { - /// End line number (1-based, inclusive). - [JsonPropertyName("end")] - public long End { get; set; } + /// Sub-agent instance identifier (the envelope agentId of its events) of the agent that issued the call; absent for calls issued by the root agent. + [JsonPropertyName("agentId")] + public string? AgentId { get; set; } - /// Start line number (1-based). - [JsonPropertyName("start")] - public long Start { get; set; } -} + /// Arguments to pass to the external tool. + [JsonPropertyName("arguments")] + public JsonElement? Arguments { get; set; } -/// File attachment. -/// The file variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentFile : PushAttachment -{ - /// - [JsonIgnore] - public override string Type => "file"; + /// Stable identity of the provider that offered the tool, for hosts that route extension-owned tools by provider. + [JsonPropertyName("providerId")] + public string? ProviderId { get; set; } - /// User-facing display name for the attachment. - [JsonPropertyName("displayName")] - public required string DisplayName { get; set; } + /// Request ID to pass to session.tools.handlePendingToolCall. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; - /// Optional line range to scope the attachment to a specific section of the file. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("lineRange")] - public PushAttachmentFileLineRange? LineRange { get; set; } + /// Tool call ID assigned to this external tool invocation. + [JsonPropertyName("toolCallId")] + public string ToolCallId { get; set; } = string.Empty; - /// Absolute file path. - [JsonPropertyName("path")] - public required string Path { get; set; } + /// Name of the external tool to invoke. + [JsonPropertyName("toolName")] + public string ToolName { get; set; } = string.Empty; } -/// Directory attachment. -/// The directory variant of . +/// External tool calls still waiting for a result. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentDirectory : PushAttachment +public sealed class PendingExternalToolRequestList { - /// - [JsonIgnore] - public override string Type => "directory"; - - /// User-facing display name for the attachment. - [JsonPropertyName("displayName")] - public required string DisplayName { get; set; } - - /// Absolute directory path. - [JsonPropertyName("path")] - public required string Path { get; set; } + /// External tool calls of the session and its sub-agents that session.tools.handlePendingToolCall would still accept, in the order they were requested. Includes calls that were pending when the session was last suspended and resumed with pending work continued, so a host that did not observe the original external_tool.requested event can answer them. + [JsonPropertyName("items")] + public IList Items { get => field ??= []; set; } } -/// End position of the selection. +/// No parameters; returns the external tool calls of the session and its sub-agents that are still waiting for a result. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PushAttachmentSelectionDetailsEnd +internal sealed class ToolsListPendingRequestsRequest { - /// End character offset within the line (0-based). - [JsonPropertyName("character")] - public long Character { get; set; } - - /// End line number (0-based). - [JsonPropertyName("line")] - public long Line { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Start position of the selection. +/// 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. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PushAttachmentSelectionDetailsStart +public sealed class ToolsInitializeAndValidateResult { - /// Start character offset within the line (0-based). - [JsonPropertyName("character")] - public long Character { get; set; } - - /// Start line number (0-based). - [JsonPropertyName("line")] - public long Line { get; set; } } -/// Position range of the selection within the file. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PushAttachmentSelectionDetails +internal sealed class SessionToolsInitializeAndValidateRequest { - /// End position of the selection. - [JsonPropertyName("end")] - public PushAttachmentSelectionDetailsEnd End { get => field ??= new(); set; } - - /// Start position of the selection. - [JsonPropertyName("start")] - public PushAttachmentSelectionDetailsStart Start { get => field ??= new(); set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Code selection attachment from an editor. -/// The selection variant of . +/// Lightweight metadata for a currently initialized session tool. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentSelection : PushAttachment +public sealed class CurrentToolMetadata { - /// - [JsonIgnore] - public override string Type => "selection"; + /// Whether the tool is loaded on demand via tool search. + [JsonPropertyName("deferLoading")] + public bool? DeferLoading { get; set; } - /// User-facing display name for the selection. - [JsonPropertyName("displayName")] - public required string DisplayName { get; set; } + /// Tool description. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; - /// Absolute path to the file containing the selection. - [JsonPropertyName("filePath")] - public required string FilePath { get; set; } + /// JSON Schema for tool input. + [JsonPropertyName("input_schema")] + public IDictionary? InputSchema { get; set; } - /// Position range of the selection within the file. - [JsonPropertyName("selection")] - public required PushAttachmentSelectionDetails Selection { get; set; } + /// MCP server name for MCP-backed tools. + [JsonPropertyName("mcpServerName")] + public string? McpServerName { get; set; } - /// The selected text content. - [JsonPropertyName("text")] - public required string Text { get; set; } + /// Raw MCP tool name for MCP-backed tools. + [JsonPropertyName("mcpToolName")] + public string? McpToolName { get; set; } + + /// Model-facing tool name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Optional MCP/config namespaced tool name. + [JsonPropertyName("namespacedName")] + public string? NamespacedName { get; set; } } -/// GitHub issue, pull request, or discussion reference. -/// The github_reference variant of . +/// Current lightweight tool metadata snapshot for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubReference : PushAttachment +public sealed class ToolsGetCurrentMetadataResult { - /// - [JsonIgnore] - public override string Type => "github_reference"; - - /// Issue, pull request, or discussion number. - [JsonPropertyName("number")] - public required long Number { get; set; } - - /// Type of GitHub reference. - [JsonPropertyName("referenceType")] - public required PushAttachmentGitHubReferenceType ReferenceType { get; set; } - - /// Current state of the referenced item (e.g., open, closed, merged). - [JsonPropertyName("state")] - public required string State { get; set; } - - /// Title of the referenced item. - [JsonPropertyName("title")] - public required string Title { get; set; } - - /// URL to the referenced item on GitHub. - [JsonPropertyName("url")] - public required string Url { get; set; } + /// Current tool metadata, or null when tools have not been initialized yet. + [JsonPropertyName("tools")] + public IList? Tools { get; set; } } -/// Pointer to a GitHub repository. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PushGitHubRepoRef +internal sealed class SessionToolsGetCurrentMetadataRequest { - /// Numeric GitHub repository id. - [JsonPropertyName("id")] - public long? Id { get; set; } - - /// Repository name (without owner). - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - /// Repository owner login (user or organization). - [JsonPropertyName("owner")] - public string Owner { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Pointer to a GitHub commit. -/// The github_commit variant of . +/// Empty result after replacing the calling connection's externally implemented tools. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubCommit : PushAttachment +public sealed class ToolsSetResult { - /// - [JsonIgnore] - public override string Type => "github_commit"; - - /// First line of the commit message. - [JsonPropertyName("message")] - public required string Message { get; set; } - - /// Full commit SHA. - [JsonPropertyName("oid")] - public required string Oid { get; set; } - - /// Repository the commit belongs to. - [JsonPropertyName("repo")] - public required PushGitHubRepoRef Repo { get; set; } - - /// URL to the commit on GitHub. - [JsonPropertyName("url")] - public required string Url { get; set; } } -/// Pointer to a GitHub release. -/// The github_release variant of . +/// Serializable definition of a caller-implemented tool whose execution is handled over the SDK connection. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubRelease : PushAttachment +public sealed class ProtocolExternalToolDefinition { - /// - [JsonIgnore] - public override string Type => "github_release"; + /// Tool-loading deferral policy. + [JsonPropertyName("defer")] + public ProtocolExternalToolDefer? Defer { get; set; } - /// Human-readable release name. - [JsonPropertyName("name")] - public required string Name { get; set; } + /// Model-visible explanation of what the tool does. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; - /// Repository the release belongs to. - [JsonPropertyName("repo")] - public required PushGitHubRepoRef Repo { get; set; } + /// Whether the tool executes commands in a terminal. + [JsonPropertyName("isTerminal")] + public bool? IsTerminal { get; set; } - /// Git tag the release is anchored to. - [JsonPropertyName("tagName")] - public required string TagName { get; set; } + /// Optional caller-defined metadata associated with the tool. + [JsonPropertyName("metadata")] + public IDictionary? Metadata { get; set; } - /// URL to the release on GitHub. - [JsonPropertyName("url")] - public required string Url { get; set; } -} + /// Unique model-visible tool name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; -/// Pointer to a GitHub Actions job. -/// The github_actions_job variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubActionsJob : PushAttachment -{ - /// - [JsonIgnore] - public override string Type => "github_actions_job"; + /// Whether this definition replaces a built-in tool with the same name. + [JsonPropertyName("overridesBuiltInTool")] + public bool? OverridesBuiltInTool { get; set; } - /// Terminal conclusion of the job when finished (e.g., success, failure, cancelled). Absent for in-progress jobs. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("conclusion")] - public string? Conclusion { get; set; } + /// JSON Schema describing the tool's input arguments. + [JsonPropertyName("parameters")] + public IDictionary? Parameters { get; set; } - /// Job id within the workflow run. - [JsonPropertyName("jobId")] - public required long JobId { get; set; } + /// Whether execution bypasses the normal tool permission prompt. + [JsonPropertyName("skipPermission")] + public bool? SkipPermission { get; set; } - /// Display name of the job. - [JsonPropertyName("jobName")] - public required string JobName { get; set; } + /// Optional human-readable display title. + [JsonPropertyName("title")] + public string? Title { get; set; } +} - /// Repository the workflow run belongs to. - [JsonPropertyName("repo")] - public required PushGitHubRepoRef Repo { get; set; } +/// Complete externally implemented tool list for the calling connection. An empty list removes every tool previously supplied by that connection. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ToolsSetRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// URL to the job on GitHub. - [JsonPropertyName("url")] - public required string Url { get; set; } + /// Complete replacement list for the calling connection. + [JsonPropertyName("tools")] + public IList Tools { get => field ??= []; set; } +} - /// Display name of the workflow the job ran in. - [JsonPropertyName("workflowName")] - public required string WorkflowName { get; set; } +/// Empty result after applying subagent settings. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ToolsUpdateSubagentSettingsResult +{ } -/// Pointer to a GitHub repository. -/// The github_repository variant of . +/// Subagent model, reasoning effort, context tier, and auto-invocation settings. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubRepository : PushAttachment +public sealed class SubagentSettingsEntry { - /// - [JsonIgnore] - public override string Type => "github_repository"; + /// Whether this agent's runtime-defined proactive invocation prompting is enabled, if supported. Currently consumed by the built-in rubber-duck agent. + [JsonPropertyName("autoInvoke")] + public bool? AutoInvoke { get; set; } - /// Short description of the repository. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("description")] - public string? Description { get; set; } + /// Context tier override for matching subagents. + [JsonPropertyName("contextTier")] + public SubagentSettingsEntryContextTier? ContextTier { get; set; } - /// Git ref this attachment is anchored at (branch, tag, or commit). When absent the default branch is implied. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("ref")] - public string? Ref { get; set; } + /// Reasoning effort override for matching subagents. + [JsonPropertyName("effortLevel")] + public string? EffortLevel { get; set; } - /// Repository pointer. - [JsonPropertyName("repo")] - public required PushGitHubRepoRef Repo { get; set; } + /// Model override for matching subagents. + [JsonPropertyName("model")] + public string? Model { get; set; } - /// URL to the repository on GitHub. - [JsonPropertyName("url")] - public required string Url { get; set; } + /// Whether the configured model strategy is preferred or required. + [JsonPropertyName("modelPolicy")] + public AgentModelPolicy? ModelPolicy { get; set; } } -/// One side of a file diff (head or base). -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PushAttachmentGitHubFileDiffSide +/// Configured per-agent subagent overrides. +public sealed class UpdateSubagentSettingsRequestSubagents { - /// Repository-relative path to the file. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Per-agent settings keyed by subagent agent_type. + [JsonPropertyName("agents")] + public IDictionary? Agents { get; set; } - /// Git ref (branch, tag, or commit SHA) the file is read at. - [JsonPropertyName("ref")] - public string Ref { get; set; } = string.Empty; + /// Names of subagents the user has turned off; they cannot be dispatched. + [JsonPropertyName("disabledSubagents")] + public IList? DisabledSubagents { get; set; } - /// Repository the file lives in. - [JsonPropertyName("repo")] - public PushGitHubRepoRef Repo { get => field ??= new(); set; } + /// Maximum number of subagents that can run concurrently; applies to usage-based billing users only. + [JsonPropertyName("maxConcurrency")] + public int? MaxConcurrency { get; set; } + + /// Maximum subagent nesting depth; applies to usage-based billing users only. + [JsonPropertyName("maxDepth")] + public int? MaxDepth { get; set; } } -/// Pointer to a single-file diff. At least one of `head` and `base` must be present. -/// The github_file_diff variant of . +/// Subagent settings to apply to the current session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubFileDiff : PushAttachment +internal sealed class UpdateSubagentSettingsRequest { - /// - [JsonIgnore] - public override string Type => "github_file_diff"; - - /// File location on the base side of the diff. Absent for additions. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("base")] - public PushAttachmentGitHubFileDiffSide? Base { get; set; } - - /// File location on the head side of the diff. Absent for deletions. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("head")] - public PushAttachmentGitHubFileDiffSide? Head { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// URL to the diff on GitHub (e.g., a commit, compare, or PR-file URL). - [JsonPropertyName("url")] - public required string Url { get; set; } + /// Subagent settings to apply, or null to clear the live session override. + [JsonPropertyName("subagents")] + public UpdateSubagentSettingsRequestSubagents? Subagents { get; set; } } -/// One side of a tree comparison (head or base). +/// RPC data type for SessionCommandsList operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PushAttachmentGitHubTreeComparisonSide +public sealed class SessionCommandsListRequest { - /// Repository the revision belongs to. - [JsonPropertyName("repo")] - public PushGitHubRepoRef Repo { get => field ??= new(); set; } + /// Include runtime built-in commands. + [JsonPropertyName("includeBuiltins")] + public bool? IncludeBuiltins { get; set; } - /// Git revision (branch, tag, or commit SHA). - [JsonPropertyName("revision")] - public string Revision { get; set; } = string.Empty; + /// Include commands registered by protocol clients, including SDK clients and extensions. + [JsonPropertyName("includeClientCommands")] + public bool? IncludeClientCommands { get; set; } + + /// Include enabled user-invocable skills and commands. + [JsonPropertyName("includeSkills")] + public bool? IncludeSkills { get; set; } } -/// Pointer to a comparison between two git revisions. -/// The github_tree_comparison variant of . +/// RPC data type for SessionCommandsListRequestWithSession operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubTreeComparison : PushAttachment +internal sealed class SessionCommandsListRequestWithSession { - /// - [JsonIgnore] - public override string Type => "github_tree_comparison"; + /// Include runtime built-in commands. + [JsonPropertyName("includeBuiltins")] + public bool? IncludeBuiltins { get; set; } - /// Base side of the comparison. - [JsonPropertyName("base")] - public required PushAttachmentGitHubTreeComparisonSide Base { get; set; } + /// Include commands registered by protocol clients, including SDK clients and extensions. + [JsonPropertyName("includeClientCommands")] + public bool? IncludeClientCommands { get; set; } - /// Head side of the comparison. - [JsonPropertyName("head")] - public required PushAttachmentGitHubTreeComparisonSide Head { get; set; } + /// Include enabled user-invocable skills and commands. + [JsonPropertyName("includeSkills")] + public bool? IncludeSkills { get; set; } - /// URL to the comparison on GitHub. - [JsonPropertyName("url")] - public required string Url { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Generic GitHub URL reference. -/// The github_url variant of . +/// Result of invoking the slash command (text output, prompt to send to the agent, completion, or subcommand selection). +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubUrl : PushAttachment +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(SlashCommandInvocationResultText), "text")] +[JsonDerivedType(typeof(SlashCommandInvocationResultAgentPrompt), "agent-prompt")] +[JsonDerivedType(typeof(SlashCommandInvocationResultCompleted), "completed")] +[JsonDerivedType(typeof(SlashCommandInvocationResultSelectSubcommand), "select-subcommand")] +[JsonDerivedType(typeof(SlashCommandInvocationResultAddTimelineEntry), "add-timeline-entry")] +[JsonDerivedType(typeof(SlashCommandInvocationResultShowDialog), "show-dialog")] +[JsonDerivedType(typeof(SlashCommandInvocationResultSetModel), "set-model")] +[JsonDerivedType(typeof(SlashCommandInvocationResultSetPlanModel), "set-plan-model")] +public partial class SlashCommandInvocationResult { - /// - [JsonIgnore] - public override string Type => "github_url"; - - /// URL to the GitHub resource. - [JsonPropertyName("url")] - public required string Url { get; set; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// Pointer to a file in a GitHub repository at a specific ref. -/// The github_file variant of . + +/// Slash-command invocation result containing text output plus Markdown/ANSI rendering flags. +/// The text variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubFile : PushAttachment +public partial class SlashCommandInvocationResultText : SlashCommandInvocationResult { /// [JsonIgnore] - public override string Type => "github_file"; + public override string Kind => "text"; - /// Repository-relative path to the file. - [JsonPropertyName("path")] - public required string Path { get; set; } + /// Whether text contains Markdown. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("markdown")] + public bool? Markdown { get; set; } - /// Git ref the file is read at (branch, tag, or commit SHA). - [JsonPropertyName("ref")] - public required string Ref { get; set; } + /// Whether ANSI sequences should be preserved. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("preserveAnsi")] + public bool? PreserveAnsi { get; set; } - /// Repository the file lives in. - [JsonPropertyName("repo")] - public required PushGitHubRepoRef Repo { get; set; } + /// True when the invocation mutated user runtime settings; consumers caching settings should refresh. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("runtimeSettingsChanged")] + public bool? RuntimeSettingsChanged { get; set; } - /// URL to the file on GitHub. - [JsonPropertyName("url")] - public required string Url { get; set; } + /// Present when the invocation changed the sandbox for this session only. Nothing was persisted, so consumers must mirror the change onto the live session rather than reloading settings, and must not treat it as a settings change. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("sandboxSessionChange")] + public SandboxSessionChange? SandboxSessionChange { get; set; } + + /// Text output for the client to render. + [JsonPropertyName("text")] + public required string Text { get; set; } } -/// Pointer to a line range inside a file in a GitHub repository. -/// The github_snippet variant of . +/// Slash-command invocation result that submits an agent prompt, with display prompt, optional mode, optional user-facing notice, and settings-change flag. +/// The agent-prompt variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubSnippet : PushAttachment +public partial class SlashCommandInvocationResultAgentPrompt : SlashCommandInvocationResult { /// [JsonIgnore] - public override string Type => "github_snippet"; - - /// Line range the snippet covers. - [JsonPropertyName("lineRange")] - public required PushAttachmentFileLineRange LineRange { get; set; } + public override string Kind => "agent-prompt"; - /// Repository-relative path to the file. - [JsonPropertyName("path")] - public required string Path { get; set; } + /// Prompt text to display to the user. + [JsonPropertyName("displayPrompt")] + public required string DisplayPrompt { get; set; } - /// Git ref the file is read at (branch, tag, or commit SHA). - [JsonPropertyName("ref")] - public required string Ref { get; set; } + /// Optional target session mode for the agent prompt. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("mode")] + public SessionMode? Mode { get; set; } - /// Repository the file lives in. - [JsonPropertyName("repo")] - public required PushGitHubRepoRef Repo { get; set; } + /// Optional user-facing notice to show before the prompt is submitted. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("notice")] + public string? Notice { get; set; } - /// URL to the snippet on GitHub (with line anchor). - [JsonPropertyName("url")] - public required string Url { get; set; } + /// Prompt to submit to the agent. + [JsonPropertyName("prompt")] + public required string Prompt { get; set; } + + /// True when the invocation mutated user runtime settings; consumers caching settings should refresh. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("runtimeSettingsChanged")] + public bool? RuntimeSettingsChanged { get; set; } } -/// Blob attachment with inline base64-encoded data. -/// The blob variant of . +/// Slash-command invocation result indicating completion, with optional message and settings-change flag. +/// The completed variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentBlob : PushAttachment +public partial class SlashCommandInvocationResultCompleted : SlashCommandInvocationResult { /// [JsonIgnore] - public override string Type => "blob"; + public override string Kind => "completed"; - /// Base64-encoded content. - [Base64String] - [JsonPropertyName("data")] - public required string Data { get; set; } + /// Optional user-facing message describing the completed command. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("message")] + public string? Message { get; set; } - /// User-facing display name for the attachment. + /// Optional target session mode applied without submitting an agent prompt. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("displayName")] - public string? DisplayName { get; set; } + [JsonPropertyName("mode")] + public SessionMode? Mode { get; set; } - /// MIME type of the inline data. - [JsonPropertyName("mimeType")] - public required string MimeType { get; set; } + /// True when the invocation mutated user runtime settings; consumers caching settings should refresh. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("runtimeSettingsChanged")] + public bool? RuntimeSettingsChanged { get; set; } } -/// Slim input shape for extension_context attachments; identity fields are runtime-derived. -/// The extension_context variant of . +/// Selectable slash-command subcommand option with name, description, and optional group label. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentExtensionContext : PushAttachment +public sealed class SlashCommandSelectSubcommandOption +{ + /// Human-readable description of the subcommand. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; + + /// Optional group label for organizing options. + [JsonPropertyName("group")] + public string? Group { get; set; } + + /// Subcommand name to invoke. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; +} + +/// Slash-command invocation result asking the client to present subcommand options for a parent command. +/// The select-subcommand variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class SlashCommandInvocationResultSelectSubcommand : SlashCommandInvocationResult { /// [JsonIgnore] - public override string Type => "extension_context"; + public override string Kind => "select-subcommand"; - /// Caller-supplied JSON payload (required, may be null but not undefined). - [JsonPropertyName("payload")] - public required JsonElement Payload { get; set; } + /// Parent command name that requires subcommand selection. + [JsonPropertyName("command")] + public required string Command { get; set; } - /// Human-readable composer pill label. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] + /// Available subcommand options for the client to present. + [JsonPropertyName("options")] + public required IList Options { get; set; } + + /// True when the invocation mutated user runtime settings; consumers caching settings should refresh. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("runtimeSettingsChanged")] + public bool? RuntimeSettingsChanged { get; set; } + + /// Human-readable title for the selection UI. [JsonPropertyName("title")] public required string Title { get; set; } } -/// Parameters for session.extensions.sendAttachmentsToMessage. +/// RPC data type for SlashCommandTimelineEntry operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SendAttachmentsToMessageParams +public sealed class SlashCommandTimelineEntry { - /// Attachments to push into the next user-message turn. extension_context entries take the slim shape; standard variants take their full AttachmentSchema shape. - [JsonPropertyName("attachments")] - public IList Attachments { get => field ??= []; set; } + /// What the user must do to recover, when the entry reports a failure the runtime knows an action for. The `text` never names a client affordance, so a client that offers one renders it from this value. + [JsonPropertyName("remediation")] + public RemediationAction? Remediation { get; set; } - /// Optional canvas instance binding the push for provenance. When supplied, the runtime resolves the canvas, verifies it is owned by the calling extension, and stamps canvasId/instanceId onto each extension_context entry. When omitted, no resolution runs and those fields stay unset on the attachment. - [JsonPropertyName("instanceId")] - public string? InstanceId { get; set; } + /// Text displayed for the timeline entry. + [JsonPropertyName("text")] + public string Text { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Timeline entry presentation type. + [JsonPropertyName("type")] + public string Type { get; set; } = string.Empty; + + /// Optional URL associated with the timeline entry. + [JsonPropertyName("url")] + public string? Url { get; set; } } -/// A tool name and arguments to execute through the session's native invocation pipeline. +/// The add-timeline-entry variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ToolsExecuteRequest +public partial class SlashCommandInvocationResultAddTimelineEntry : SlashCommandInvocationResult { - /// Arguments supplied to the tool. - [JsonPropertyName("arguments")] - public JsonElement Arguments { get; set; } + /// + [JsonIgnore] + public override string Kind => "add-timeline-entry"; - /// Name of the currently offered tool to execute. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Timeline entry the host should append. + [JsonPropertyName("entry")] + public required SlashCommandTimelineEntry Entry { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Optional text the host should prefill into the input editor. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("prefillInput")] + public string? PrefillInput { get; set; } - /// Optional identifier used to correlate this invocation with its tool call. - [JsonPropertyName("toolCallId")] - public string? ToolCallId { get; set; } + /// Whether command execution changed persisted runtime settings. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("runtimeSettingsChanged")] + public bool? RuntimeSettingsChanged { get; set; } } -/// Custom grammar input format accepted by a built-in tool. +/// RPC data type for SlashCommandModelPickerDialog operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class BuiltinToolFormat +public sealed class SlashCommandModelPickerDialog { - /// Grammar definition accepted by the tool. - [JsonPropertyName("definition")] - public string Definition { get; set; } = string.Empty; + /// Discriminator for a model-picker dialog. + [JsonPropertyName("kind")] + public string Kind { get; set; } = string.Empty; - /// Grammar syntax used by the format definition. - [JsonPropertyName("syntax")] - public string Syntax { get; set; } = string.Empty; + /// Model that should be enabled before it can be selected. + [JsonPropertyName("modelToEnable")] + public string? ModelToEnable { get; set; } - /// Custom input-format discriminator. - [JsonPropertyName("type")] - public BuiltinToolFormatType Type { get; set; } + /// Settings scope the picker should modify. + [JsonPropertyName("scope")] + public string? Scope { get; set; } + + /// Model-selection target represented by the picker. + [JsonPropertyName("target")] + public string? Target { get; set; } } -/// JSON Schema object accepted by a built-in tool. +/// The show-dialog variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class BuiltinToolInputSchema +public partial class SlashCommandInvocationResultShowDialog : SlashCommandInvocationResult { - /// Root type of the tool input schema. - [JsonPropertyName("type")] - public BuiltinToolInputSchemaType Type { get; set; } + /// + [JsonIgnore] + public override string Kind => "show-dialog"; + + /// Dialog the host should display. + [JsonPropertyName("dialog")] + public required SlashCommandModelPickerDialog Dialog { get; set; } + + /// Whether command execution changed persisted runtime settings. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("runtimeSettingsChanged")] + public bool? RuntimeSettingsChanged { get; set; } } -/// Rust-owned metadata and input schema for a built-in tool. +/// The set-model variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class BuiltinToolDescriptor +public partial class SlashCommandInvocationResultSetModel : SlashCommandInvocationResult { - /// Model-facing description of the tool's behavior. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; - - /// Optional custom input format used instead of a JSON Schema. - [JsonPropertyName("format")] - public BuiltinToolFormat? Format { get; set; } - - /// Whether the tool provides a specialized intention summary. - [JsonPropertyName("hasSummariseIntention")] - public bool HasSummariseIntention { get; set; } + /// + [JsonIgnore] + public override string Kind => "set-model"; - /// JSON Schema for the tool input, or null when the tool uses a custom format. - [JsonPropertyName("inputSchema")] - public BuiltinToolInputSchema? InputSchema { get; set; } + /// Auto routing profile selected by the command, when the model is Auto. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("autoTier")] + public AutoTier? AutoTier { get; set; } - /// Optional supplemental usage instructions for the tool. - [JsonPropertyName("instructions")] - public string? Instructions { get; set; } + /// Model selected by the command. + [JsonPropertyName("model")] + public required string Model { get; set; } - /// Whether the tool executes commands in a terminal. - [JsonPropertyName("isTerminal")] - public bool IsTerminal { get; set; } + /// Reasoning effort selected for the model. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("reasoningEffort")] + public string? ReasoningEffort { get; set; } - /// Stable name used to invoke the built-in tool. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Repository settings scope modified by the command. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("repoScope")] + public string? RepoScope { get; set; } - /// Policy describing which tool metadata may be recorded without obfuscation. - [JsonPropertyName("safeForTelemetry")] - public JsonElement SafeForTelemetry { get; set; } + /// User-settings snapshot to restore if the host cancels the model switch. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("revertOnCancel")] + public IDictionary? RevertOnCancel { get; set; } - /// Optional human-readable title for the tool. - [JsonPropertyName("title")] - public string? Title { get; set; } + /// Whether command execution changed persisted runtime settings. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("runtimeSettingsChanged")] + public bool? RuntimeSettingsChanged { get; set; } - /// Optional tool category discriminator. - [JsonPropertyName("type")] - public string? Type { get; set; } -} + /// Settings scope modified by the command. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("scope")] + public string? Scope { get; set; } -/// Rust-owned built-in tool descriptors for the session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ToolsGetBuiltinDescriptorsResult -{ - /// Built-in tool descriptors materialized for the session. - [JsonPropertyName("tools")] - public IList Tools { get => field ??= []; set; } + /// User-facing warning produced while selecting the model. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("warning")] + public string? Warning { get; set; } } -/// Shell-specific names and description lines used to materialize built-in shell tool descriptors. +/// The set-plan-model variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ToolsShellDescriptorConfig +public partial class SlashCommandInvocationResultSetPlanModel : SlashCommandInvocationResult { - /// Additional model-facing shell description lines. - [JsonPropertyName("descriptionLines")] - public IList DescriptionLines { get => field ??= []; set; } - - /// Human-readable shell name. - [JsonPropertyName("displayName")] - public string DisplayName { get; set; } = string.Empty; - - /// Tool name used to list active shells. - [JsonPropertyName("listShellsToolName")] - public string ListShellsToolName { get; set; } = string.Empty; - - /// Tool name used to read shell output. - [JsonPropertyName("readShellToolName")] - public string ReadShellToolName { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "set-plan-model"; - /// Tool name used to start shell commands. - [JsonPropertyName("shellToolName")] - public string ShellToolName { get; set; } = string.Empty; + /// User-facing confirmation message for the plan-model selection. + [JsonPropertyName("message")] + public required string Message { get; set; } - /// Stable shell type identifier. - [JsonPropertyName("shellType")] - public string ShellType { get; set; } = string.Empty; + /// Dedicated model selected for plan mode. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("planModel")] + public string? PlanModel { get; set; } - /// Tool name used to stop shell commands. - [JsonPropertyName("stopShellToolName")] - public string StopShellToolName { get; set; } = string.Empty; + /// Whether command execution changed persisted runtime settings. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("runtimeSettingsChanged")] + public bool? RuntimeSettingsChanged { get; set; } } -/// Options controlling how Rust-owned built-in tool descriptors are materialized. +/// Slash command name and optional raw input string to invoke. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ToolsGetBuiltinDescriptorsRequest +internal sealed class CommandsInvokeRequest { - /// Whether background task completion notifications are enabled. - [JsonPropertyName("backgroundTaskNotificationsEnabled")] - public bool? BackgroundTaskNotificationsEnabled { get; set; } + /// Raw input after the command name. + [JsonPropertyName("input")] + public string? Input { get; set; } - /// Whether tool descriptors should include authoring metadata. - [JsonPropertyName("includeAuthor")] - public bool? IncludeAuthor { get; set; } + /// Command name. Leading slashes are stripped and the name is matched case-insensitively. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Whether descriptors should favor fewer user-intervention prompts. - [JsonPropertyName("reduceUserIntervention")] - public bool? ReduceUserIntervention { get; set; } + /// Optional client surface that initiated the invocation. + [JsonPropertyName("origin")] + public CommandsInvocationOrigin? Origin { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// Shell-specific names and description lines for shell tools. - [JsonPropertyName("shellConfig")] - public ToolsShellDescriptorConfig? ShellConfig { get; set; } - - /// Whether the configured shell supports PowerShell 7 syntax. - [JsonPropertyName("shellSupportsPowerShell7Syntax")] - public bool? ShellSupportsPowerShell7Syntax { get; set; } - - /// Default shell timeout in milliseconds. - [JsonPropertyName("shellTimeoutMs")] - public double? ShellTimeoutMs { get; set; } - - /// Whether semantic skill lookup is available. - [JsonPropertyName("skillEmbeddingEnabled")] - public bool? SkillEmbeddingEnabled { get; set; } } -/// Task completion notification with summary from the agent. +/// Whether finalizing the invocation effect succeeded, and the failure reason when it did not. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TaskCompleteData +internal sealed class CommandsFinalizeInvocationEffectResult { - /// Structured blocker details when outcome is blocked. - [JsonPropertyName("blocker")] - public TaskBlocker? Blocker { get; set; } - - /// Active autopilot objective ID evaluated by the completion reviewer. - [JsonPropertyName("objectiveId")] - public long? ObjectiveId { get; set; } - - /// Semantic completion decision. Absent on legacy events and invalid tool calls. - [JsonPropertyName("outcome")] - public TaskCompletionOutcome? Outcome { get; set; } - - /// Label-safe runtime rationale for the completion decision (e.g. a cancellation or pause/resume downgrade), when one applies. Reviewer-authored rationale is intentionally omitted here because this event has no IFC label channel; the reviewer's findings remain available through its own labeled sub-agent events. - [JsonPropertyName("reason")] - public string? Reason { get; set; } + /// Failure reason when the invocation effect could not be finalized. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Whether the task was accepted as complete. False when validation failed or completion was rejected or blocked by the reviewer. + /// Whether the pending invocation effect was finalized successfully. [JsonPropertyName("success")] - public bool? Success { get; set; } - - /// Summary of the completed task, provided by the agent. - [JsonPropertyName("summary")] - public string? Summary { get; set; } + public bool Success { get; set; } } -/// Binary result returned by a tool for the model. +/// The pending slash-command invocation effect to finalize, plus whether the host applied or cancelled it. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ExternalToolTextResultForLlmBinaryResultsForLlm +internal sealed class CommandsFinalizeInvocationEffectRequest { - /// Base64-encoded binary data. - [Base64String] - [JsonPropertyName("data")] - public string Data { get; set; } = string.Empty; - - /// Human-readable description of the binary data. - [JsonPropertyName("description")] - public string? Description { get; set; } - - /// Optional metadata from the producing tool. - [JsonPropertyName("metadata")] - public IDictionary? Metadata { get; set; } + /// The slash-command result object that produced the pending effect, echoed back unchanged. + [JsonPropertyName("effect")] + public IDictionary Effect { get => field ??= new Dictionary(); set; } - /// MIME type of the binary data. - [JsonPropertyName("mimeType")] - public string MimeType { get; set; } = string.Empty; + /// Whether the host applied or cancelled the pending invocation effect. + [JsonPropertyName("outcome")] + public CommandsInvocationEffectOutcome Outcome { get; set; } - /// Binary result type discriminator. Use "image" for images and "resource" for other binary data. - [JsonPropertyName("type")] - public ExternalToolTextResultForLlmBinaryResultsForLlmType Type { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// A content block within a tool result, which may be text, terminal output, image, audio, or a resource. -/// Polymorphic base type discriminated by type. +/// Indicates whether the pending client-handled command was completed successfully. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "type", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(ExternalToolTextResultForLlmContentText), "text")] -[JsonDerivedType(typeof(ExternalToolTextResultForLlmContentTerminal), "terminal")] -[JsonDerivedType(typeof(ExternalToolTextResultForLlmContentShellExit), "shell_exit")] -[JsonDerivedType(typeof(ExternalToolTextResultForLlmContentImage), "image")] -[JsonDerivedType(typeof(ExternalToolTextResultForLlmContentAudio), "audio")] -[JsonDerivedType(typeof(ExternalToolTextResultForLlmContentResourceLink), "resource_link")] -[JsonDerivedType(typeof(ExternalToolTextResultForLlmContentResource), "resource")] -public partial class ExternalToolTextResultForLlmContent +public sealed class CommandsHandlePendingCommandResult { - /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { get; set; } = string.Empty; + /// Whether the command was handled successfully. + [JsonPropertyName("success")] + public bool Success { get; set; } } - -/// Plain text content block. -/// The text variant of . +/// Pending command request ID and an optional error if the client handler failed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ExternalToolTextResultForLlmContentText : ExternalToolTextResultForLlmContent +internal sealed class CommandsHandlePendingCommandRequest { - /// - [JsonIgnore] - public override string Type => "text"; + /// Error message if the command handler failed. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// The text content. - [JsonPropertyName("text")] - public required string Text { get; set; } + /// Request ID from the command invocation event. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Terminal/shell output content block with optional exit code and working directory. -/// The terminal variant of . +/// Error message produced while executing the command, if any. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ExternalToolTextResultForLlmContentTerminal : ExternalToolTextResultForLlmContent +public sealed class ExecuteCommandResult { - /// - [JsonIgnore] - public override string Type => "terminal"; - - /// Working directory where the command was executed. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("cwd")] - public string? Cwd { get; set; } - - /// Process exit code, if the command has completed. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("exitCode")] - public long? ExitCode { get; set; } - - /// Terminal/shell output text. - [JsonPropertyName("text")] - public required string Text { get; set; } + /// Error message produced while executing the command, if any. Omitted when the handler succeeded. + [JsonPropertyName("error")] + public string? Error { get; set; } } -/// Shell command exit metadata with optional output preview. -/// The shell_exit variant of . +/// Slash command name and argument string to execute synchronously. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ExternalToolTextResultForLlmContentShellExit : ExternalToolTextResultForLlmContent +internal sealed class ExecuteCommandParams { - /// - [JsonIgnore] - public override string Type => "shell_exit"; - - /// Working directory where the shell command was executed. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("cwd")] - public string? Cwd { get; set; } - - /// Exit code from the completed shell command. - [JsonPropertyName("exitCode")] - public required long ExitCode { get; set; } - - /// Path reported in the shell session's filesystem namespace when shell output exceeded the configured large-output threshold. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("outputFilePath")] - public string? OutputFilePath { get; set; } - - /// Output associated with this shell command, if available. May be partial, truncated, or a preview; not guaranteed to be full output. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("outputPreview")] - public string? OutputPreview { get; set; } + /// Argument string to pass to the command (empty string if none). + [JsonPropertyName("args")] + public string Args { get; set; } = string.Empty; - /// Whether outputPreview is known to be incomplete or truncated. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("outputTruncated")] - public bool? OutputTruncated { get; set; } + /// Name of the slash command to invoke (without the leading '/'). + [JsonPropertyName("commandName")] + public string CommandName { get; set; } = string.Empty; - /// Shell id, as assigned by Copilot runtime. - [JsonPropertyName("shellId")] - public required string ShellId { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Image content block with base64-encoded data. -/// The image variant of . +/// Indicates whether the command was accepted into the local execution queue. +/// Data type discriminated by queued. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ExternalToolTextResultForLlmContentImage : ExternalToolTextResultForLlmContent +public partial class EnqueueCommandResult { - /// - [JsonIgnore] - public override string Type => "image"; - - /// Base64-encoded image data. - [Base64String] - [JsonPropertyName("data")] - public required string Data { get; set; } + /// The boolean discriminator. + [JsonPropertyName("queued")] + public bool Queued { get; set; } - /// MIME type of the image (e.g., image/png, image/jpeg). - [JsonPropertyName("mimeType")] - public required string MimeType { get; set; } + /// Stable opaque ID of the queued command. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("queueId")] + public string? QueueId { get; set; } } -/// Audio content block with base64-encoded data. -/// The audio variant of . +/// Slash-prefixed command string to enqueue for FIFO processing. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ExternalToolTextResultForLlmContentAudio : ExternalToolTextResultForLlmContent +internal sealed class EnqueueCommandParams { - /// - [JsonIgnore] - public override string Type => "audio"; + /// Slash-prefixed command string to enqueue, e.g. '/compact' or '/model gpt-4'. Queued FIFO with any in-flight items; if the session is idle, processing kicks off immediately. + [JsonPropertyName("command")] + public string Command { get; set; } = string.Empty; - /// Base64-encoded audio data. - [Base64String] - [JsonPropertyName("data")] - public required string Data { get; set; } + /// Optional user-facing text for the queue row. The command string is shown when omitted. + [JsonPropertyName("displayText")] + public string? DisplayText { get; set; } - /// MIME type of the audio (e.g., audio/wav, audio/mpeg). - [JsonPropertyName("mimeType")] - public required string MimeType { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Icon image for a resource. +/// Indicates whether the queued-command response was matched to a pending request. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ExternalToolTextResultForLlmContentResourceLinkIcon +public sealed class CommandsRespondToQueuedCommandResult { - /// MIME type of the icon image. - [JsonPropertyName("mimeType")] - public string? MimeType { get; set; } - - /// Available icon sizes (e.g., ['16x16', '32x32']). - [JsonPropertyName("sizes")] - public IList? Sizes { get; set; } - - /// URL or path to the icon image. - [JsonPropertyName("src")] - public string Src { get; set; } = string.Empty; - - /// Theme variant this icon is intended for. - [JsonPropertyName("theme")] - public ExternalToolTextResultForLlmContentResourceLinkIconTheme? Theme { get; set; } + /// Whether a pending queued command with the given request ID was found and resolved. False when the request was already resolved, cancelled, or unknown. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Resource link content block referencing an external resource. -/// The resource_link variant of . +/// Result of the queued command execution. +/// Data type discriminated by handled. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ExternalToolTextResultForLlmContentResourceLink : ExternalToolTextResultForLlmContent +public partial class QueuedCommandResult { - /// - [JsonIgnore] - public override string Type => "resource_link"; - - /// Human-readable description of the resource. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("description")] - public string? Description { get; set; } - - /// Icons associated with this resource. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("icons")] - public IList? Icons { get; set; } + /// The boolean discriminator. + [JsonPropertyName("handled")] + public bool Handled { get; set; } - /// MIME type of the resource content. + /// When true, the runtime will not process subsequent queued commands until a new request comes in. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("mimeType")] - public string? MimeType { get; set; } - - /// Resource name identifier. - [JsonPropertyName("name")] - public required string Name { get; set; } + [JsonPropertyName("stopProcessingQueue")] + public bool? StopProcessingQueue { get; set; } +} - /// Size of the resource in bytes. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("size")] - public long? Size { get; set; } +/// Queued-command request ID and the result indicating whether the host executed it (and whether to stop processing further queued commands). +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class CommandsRespondToQueuedCommandRequest +{ + /// Request ID from the `command.queued` event the host is responding to. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; - /// Human-readable display title for the resource. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("title")] - public string? Title { get; set; } + /// Result of the queued command execution. + [JsonPropertyName("result")] + public QueuedCommandResult Result { get => field ??= new(); set; } - /// URI identifying the resource. - [JsonPropertyName("uri")] - public required string Uri { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Embedded resource content block with inline text or binary data. -/// The resource variant of . +/// Telemetry engagement ID for the session, when available. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ExternalToolTextResultForLlmContentResource : ExternalToolTextResultForLlmContent +public sealed class SessionTelemetryEngagement { - /// - [JsonIgnore] - public override string Type => "resource"; - - /// The embedded resource contents, either text or base64-encoded binary. - [JsonPropertyName("resource")] - public required JsonElement Resource { get; set; } + /// Current telemetry engagement ID, when available. + [JsonPropertyName("engagementId")] + public string? EngagementId { get; set; } } -/// A message injected by a tool result. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ToolResultNewMessage +internal sealed class SessionTelemetryGetEngagementIdRequest { - /// Message content to inject after the tool result. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; - - /// Source attributed to the injected message. - [JsonPropertyName("source")] - public string Source { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// RPC data type for TaskCompletionDecision operations. +/// Feature override key/value pairs to attach to subsequent telemetry events from this session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TaskCompletionDecision +internal sealed class TelemetrySetFeatureOverridesRequest { - /// Objective eligibility token captured when the decision was evaluated. - [JsonPropertyName("completionEligibilityToken")] - public long? CompletionEligibilityToken { get; set; } - - /// Whether completion was accepted after the reviewer-rejection budget was exhausted. - [JsonPropertyName("completionRejectionBudgetExhausted")] - public bool? CompletionRejectionBudgetExhausted { get; set; } - - /// Active autopilot objective evaluated by the completion reviewer. - [JsonPropertyName("objectiveId")] - public long? ObjectiveId { get; set; } - - /// Semantic result of evaluating the task completion request. - [JsonPropertyName("outcome")] - public TaskCompletionOutcome Outcome { get; set; } - - /// Rationale for the completion decision, when one is available. - [JsonPropertyName("reason")] - public string? Reason { get; set; } - - /// Whether the rationale was derived from completion-reviewer output. - [JsonPropertyName("reviewerDerived")] - public bool? ReviewerDerived { get; set; } + /// Override key/value pairs to attach to subsequent telemetry events from this session. Replaces any previously-set overrides. + [JsonPropertyName("features")] + public IDictionary Features { get => field ??= new Dictionary(); set; } - /// Information-flow metadata captured from the completion reviewer. - [JsonPropertyName("reviewerResultMeta")] - public JsonElement? ReviewerResultMeta { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Expanded canonical result returned by a session tool. +/// Completed transient query. Ordered chunks and the terminal outcome are also delivered through `ui.ephemeral_query` session events while it runs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ToolResultExpanded +public sealed class UIEphemeralQueryResult { - /// Base64-encoded binary results returned to the model. - [JsonPropertyName("binaryResultsForLlm")] - public IList? BinaryResultsForLlm { get; set; } - - /// Sources returned by the tool that the model may cite. - [JsonPropertyName("citableSources")] - public IList? CitableSources { get; set; } - - /// Structured content blocks returned to the model. - [JsonPropertyName("contents")] - public IList? Contents { get; set; } - - /// Error message for an unsuccessful execution. - [JsonPropertyName("error")] - public string? Error { get; set; } - - /// Metadata propagated with the tool result, including information-flow labels. - [JsonPropertyName("mcpMeta")] - public IDictionary? McpMeta { get; set; } - - /// Messages to inject after the tool result. - [JsonPropertyName("newMessages")] - public IList? NewMessages { get; set; } - - /// Whether post-tool-use failure hooks have already processed this result. - [JsonPropertyName("postToolUseFailureHooksProcessed")] - public bool? PostToolUseFailureHooksProcessed { get; set; } - - /// Execution outcome classification. - [JsonPropertyName("resultType")] - public ToolResultType ResultType { get; set; } + /// Answer returned by the model. + [JsonPropertyName("answer")] + public string Answer { get; set; } = string.Empty; +} - /// Detailed log content available for session display. - [JsonPropertyName("sessionLog")] - public string? SessionLog { get; set; } +/// Transient question to answer without adding it to conversation history. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class UIEphemeralQueryRequest +{ + /// Question to answer from the current conversation context. + [JsonPropertyName("question")] + public string Question { get; set; } = string.Empty; - /// Skill invocation metadata produced by the tool. - [JsonPropertyName("skillInvocation")] - public JsonElement? SkillInvocation { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Whether large-output post-processing should be skipped. - [JsonPropertyName("skipLargeOutputProcessing")] - public bool? SkipLargeOutputProcessing { get; set; } +/// The elicitation response (accept with form values, decline, or cancel). +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class UIElicitationResponse +{ + /// MCP response metadata. + [JsonPropertyName("_meta")] + public IDictionary? Meta { get; set; } - /// Structured result content in addition to the model-facing text. - [JsonPropertyName("structuredContent")] - public JsonElement? StructuredContent { get; set; } + /// The user's response: accept (submitted), decline (rejected), or cancel (dismissed). + [JsonPropertyName("action")] + public UIElicitationResponseAction Action { get; set; } - /// Completion-review decision produced by the task-completion tool. - [JsonPropertyName("taskCompletionDecision")] - public TaskCompletionDecision? TaskCompletionDecision { get; set; } + /// The form values submitted by the user (present when action is 'accept'). + [JsonPropertyName("content")] + public IDictionary? Content { get; set; } +} - /// Text result returned to the model. - [JsonPropertyName("textResultForLlm")] - public string TextResultForLlm { get; set; } = string.Empty; +/// JSON Schema describing the form fields to present to the user. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class UIElicitationSchema +{ + /// Form field definitions, keyed by field name. + [JsonPropertyName("properties")] + public IDictionary Properties { get => field ??= new Dictionary(); set; } - /// Deferred tool names made available by this result. - [JsonPropertyName("toolReferences")] - public IList? ToolReferences { get; set; } + /// List of required field names. + [JsonPropertyName("required")] + public IList? Required { get; set; } - /// Tool-specific telemetry payload. - [JsonPropertyName("toolTelemetry")] - public JsonElement? ToolTelemetry { get; set; } + /// Schema type indicator (always 'object'). + [JsonPropertyName("type")] + public string Type { get; set; } = string.Empty; +} - /// Optional UI resource produced by the tool. - [JsonPropertyName("uiResource")] - public JsonElement? UiResource { get; set; } +/// Metadata controlling an MCP task's lifetime. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpTaskMetadata +{ + /// Task time-to-live. + [JsonPropertyName("ttl")] + public long? Ttl { get; set; } } -/// Task-completion tool arguments and final result used to build a label-safe session event payload. +/// Prompt message and JSON schema describing the form fields to elicit from the user. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ToolsTaskCompleteEventDataRequest +internal sealed class UIElicitationRequest { - /// Final expanded result returned by the task_complete tool. - [JsonPropertyName("finalResult")] - public ToolResultExpanded FinalResult { get => field ??= new(); set; } + /// MCP request metadata. + [JsonPropertyName("_meta")] + public IDictionary? Meta { get; set; } + + /// Message describing what information is needed from the user. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; + + /// Elicitation mode. Omitted and form are equivalent for structured elicitation. + [JsonPropertyName("mode")] + public McpElicitationFormMode? Mode { get; set; } + + /// JSON Schema describing the form fields to present to the user. + [JsonPropertyName("requestedSchema")] + public UIElicitationSchema RequestedSchema { get => field ??= new(); set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - /// Arguments supplied to the completed task_complete tool call. - [JsonPropertyName("toolArgs")] - public JsonElement ToolArgs { get; set; } + /// MCP task metadata. + [JsonPropertyName("task")] + public McpTaskMetadata? Task { get; set; } } -/// Indicates whether the external tool call result was handled successfully. +/// Indicates whether the elicitation response was accepted; false if it was already resolved by another client. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HandlePendingToolCallResult +public sealed class UIElicitationResult { - /// Whether the tool call result was handled successfully. + /// Whether the response was accepted. False if the request was already resolved by another client. [JsonPropertyName("success")] public bool Success { get; set; } } -/// Pending external tool call request ID, with the tool result or an error describing why it failed. +/// Pending elicitation request ID and the user's response (accept/decline/cancel + form values). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HandlePendingToolCallRequest +internal sealed class UIHandlePendingElicitationRequest { - /// Error message if the tool call failed. - [JsonPropertyName("error")] - public string? Error { get; set; } - - /// Request ID of the pending tool call. + /// The unique request ID from the elicitation.requested event. [JsonPropertyName("requestId")] public string RequestId { get; set; } = string.Empty; - /// Tool call result (string or expanded result object). + /// The elicitation response (accept with form values, decline, or cancel). [JsonPropertyName("result")] - public JsonElement? Result { get; set; } + public UIElicitationResponse Result { get => field ??= new(); set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// External tool call of the session or one of its sub-agents that is still waiting for session.tools.handlePendingToolCall. +/// Indicates whether the pending UI request was resolved by this call. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PendingExternalToolRequest +public sealed class UIHandlePendingResult { - /// Sub-agent instance identifier (the envelope agentId of its events) of the agent that issued the call; absent for calls issued by the root agent. - [JsonPropertyName("agentId")] - public string? AgentId { get; set; } + /// True if the request was still pending and was resolved by this call. False if the request ID was unknown, already resolved by another client (e.g. GitHub), expired, or otherwise no longer pending. + [JsonPropertyName("success")] + public bool Success { get; set; } +} - /// Arguments to pass to the external tool. - [JsonPropertyName("arguments")] - public JsonElement? Arguments { get; set; } +/// User response for a pending user-input request, with answer text and whether it was typed freeform. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class UIUserInputResponse +{ + /// The user's answer text. + [JsonPropertyName("answer")] + public string Answer { get; set; } = string.Empty; - /// Stable identity of the provider that offered the tool, for hosts that route extension-owned tools by provider. - [JsonPropertyName("providerId")] - public string? ProviderId { get; set; } + /// True if the user typed a freeform response, false if they selected a presented choice. Used by telemetry to differentiate between free text input and choice selection. + [JsonPropertyName("wasFreeform")] + public bool WasFreeform { get; set; } +} - /// Request ID to pass to session.tools.handlePendingToolCall. +/// Request ID of a pending `user_input.requested` event and the user's response. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class UIHandlePendingUserInputRequest +{ + /// The unique request ID from the user_input.requested event. [JsonPropertyName("requestId")] public string RequestId { get; set; } = string.Empty; - /// Tool call ID assigned to this external tool invocation. - [JsonPropertyName("toolCallId")] - public string ToolCallId { get; set; } = string.Empty; + /// User response for a pending user-input request, with answer text and whether it was typed freeform. + [JsonPropertyName("response")] + public UIUserInputResponse Response { get => field ??= new(); set; } - /// Name of the external tool to invoke. - [JsonPropertyName("toolName")] - public string ToolName { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// External tool calls still waiting for a result. +/// Request ID of a pending `sampling.requested` event and an optional sampling result payload (omit to reject). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PendingExternalToolRequestList +internal sealed class UIHandlePendingSamplingRequest { - /// External tool calls of the session and its sub-agents that session.tools.handlePendingToolCall would still accept, in the order they were requested. Includes calls that were pending when the session was last suspended and resumed with pending work continued, so a host that did not observe the original external_tool.requested event can answer them. - [JsonPropertyName("items")] - public IList Items { get => field ??= []; set; } + /// The unique request ID from the sampling.requested event. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; + + /// Optional sampling result payload. Omit to reject/cancel the sampling request without providing a result. + [JsonPropertyName("response")] + public JsonElement? Response { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// No parameters; returns the external tool calls of the session and its sub-agents that are still waiting for a result. +/// Request ID of a pending `auto_mode_switch.requested` event and the user's response. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ToolsListPendingRequestsRequest +internal sealed class UIHandlePendingAutoModeSwitchRequest { + /// The unique request ID from the auto_mode_switch.requested event. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; + + /// User's choice for auto-mode switching: yes (allow this turn), yes_always (allow + persist as setting), or no (decline). + [JsonPropertyName("response")] + public UIAutoModeSwitchResponse Response { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// 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. +/// The user's selected action for an exhausted session limit. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ToolsInitializeAndValidateResult +public sealed class UISessionLimitsExhaustedResponse { + /// Action selected by the user. + [JsonPropertyName("action")] + public UISessionLimitsExhaustedResponseAction Action { get; set; } + + /// AI Credits to add to the current max when action is 'add'. + [JsonPropertyName("additionalAiCredits")] + public double? AdditionalAiCredits { get; set; } + + /// New absolute max AI Credits when action is 'set'. + [JsonPropertyName("maxAiCredits")] + public double? MaxAiCredits { get; set; } } -/// Identifies the target session. +/// Request ID of a pending `session_limits_exhausted.requested` event and the user's selected limit action. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionToolsInitializeAndValidateRequest +internal sealed class UIHandlePendingSessionLimitsExhaustedRequest { + /// The unique request ID from the session_limits_exhausted.requested event. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; + + /// The selected session-limit action. + [JsonPropertyName("response")] + public UISessionLimitsExhaustedResponse Response { get => field ??= new(); set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Lightweight metadata for a currently initialized session tool. +/// User response for a pending exit-plan-mode request, with approval state, selected action, auto-approve flag, and feedback. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CurrentToolMetadata +public sealed class UIExitPlanModeResponse { - /// Whether the tool is loaded on demand via tool search. - [JsonPropertyName("deferLoading")] - public bool? DeferLoading { get; set; } + /// Whether the plan was approved. + [JsonPropertyName("approved")] + public bool Approved { get; set; } - /// Tool description. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; + /// Whether subsequent edits should be auto-approved without confirmation. + [JsonPropertyName("autoApproveEdits")] + public bool? AutoApproveEdits { get; set; } - /// JSON Schema for tool input. - [JsonPropertyName("input_schema")] - public IDictionary? InputSchema { get; set; } + /// When true, the agent is instructed to end its turn without starting implementation so the client can restore the session model and auto-submit a fresh implementation turn on it. Set only when a distinct plan configuration (a different model, reasoning effort, or context tier) actually ran the planning turn. + [JsonPropertyName("deferImplementation")] + public bool? DeferImplementation { get; set; } - /// MCP server name for MCP-backed tools. - [JsonPropertyName("mcpServerName")] - public string? McpServerName { get; set; } + /// Feedback from the user when they declined the plan or requested changes. + [JsonPropertyName("feedback")] + public string? Feedback { get; set; } - /// Raw MCP tool name for MCP-backed tools. - [JsonPropertyName("mcpToolName")] - public string? McpToolName { get; set; } + /// The action the user selected. Defaults to 'autopilot' when autoApproveEdits is true, otherwise 'interactive'. + [JsonPropertyName("selectedAction")] + public UIExitPlanModeAction? SelectedAction { get; set; } +} - /// Model-facing tool name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; +/// Request ID of a pending `exit_plan_mode.requested` event and the user's response. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class UIHandlePendingExitPlanModeRequest +{ + /// The unique request ID from the exit_plan_mode.requested event. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; - /// Optional MCP/config namespaced tool name. - [JsonPropertyName("namespacedName")] - public string? NamespacedName { get; set; } + /// User response for a pending exit-plan-mode request, with approval state, selected action, auto-approve flag, and feedback. + [JsonPropertyName("response")] + public UIExitPlanModeResponse Response { get => field ??= new(); set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Current lightweight tool metadata snapshot for the session. +/// 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). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ToolsGetCurrentMetadataResult +public sealed class UIRegisterDirectAutoModeSwitchHandlerResult { - /// Current tool metadata, or null when tools have not been initialized yet. - [JsonPropertyName("tools")] - public IList? Tools { get; set; } + /// Opaque handle representing the registration. Pass this same handle to `unregisterDirectAutoModeSwitchHandler` when the in-process handler is no longer active. Multiple registrations are reference-counted; the server bridge will only dispatch auto-mode-switch requests when no handles are active. + [JsonPropertyName("handle")] + public string Handle { get; set; } = string.Empty; } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionToolsGetCurrentMetadataRequest +internal sealed class SessionUiRegisterDirectAutoModeSwitchHandlerRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Empty result after replacing the calling connection's externally implemented tools. +/// Indicates whether the handle was active and the registration count was decremented. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ToolsSetResult +public sealed class UIUnregisterDirectAutoModeSwitchHandlerResult { + /// True if the handle was active and decremented the counter; false if the handle was unknown. + [JsonPropertyName("unregistered")] + public bool Unregistered { get; set; } } -/// Serializable definition of a caller-implemented tool whose execution is handled over the SDK connection. +/// Opaque handle previously returned by `registerDirectAutoModeSwitchHandler` to release. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProtocolExternalToolDefinition +internal sealed class UIUnregisterDirectAutoModeSwitchHandlerRequest { - /// Tool-loading deferral policy. - [JsonPropertyName("defer")] - public ProtocolExternalToolDefer? Defer { get; set; } - - /// Model-visible explanation of what the tool does. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; + /// Handle previously returned by `registerDirectAutoModeSwitchHandler`. + [JsonPropertyName("handle")] + public string Handle { get; set; } = string.Empty; - /// Whether the tool executes commands in a terminal. - [JsonPropertyName("isTerminal")] - public bool? IsTerminal { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Optional caller-defined metadata associated with the tool. - [JsonPropertyName("metadata")] - public IDictionary? Metadata { get; set; } +/// Indicates whether the operation succeeded. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PermissionsConfigureResult +{ + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } +} - /// Unique model-visible tool name. +/// Source descriptor for a `session.permissions.configure` content-exclusion rule, with source name and type. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PermissionsConfigureAdditionalContentExclusionPolicyRuleSource +{ + /// Name of the policy source. [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; - /// Whether this definition replaces a built-in tool with the same name. - [JsonPropertyName("overridesBuiltInTool")] - public bool? OverridesBuiltInTool { get; set; } - - /// JSON Schema describing the tool's input arguments. - [JsonPropertyName("parameters")] - public IDictionary? Parameters { get; set; } - - /// Whether execution bypasses the normal tool permission prompt. - [JsonPropertyName("skipPermission")] - public bool? SkipPermission { get; set; } - - /// Optional human-readable display title. - [JsonPropertyName("title")] - public string? Title { get; set; } + /// Type of the policy source. + [JsonPropertyName("type")] + public string Type { get; set; } = string.Empty; } -/// Complete externally implemented tool list for the calling connection. An empty list removes every tool previously supplied by that connection. +/// Single content-exclusion rule supplied to `session.permissions.configure`, with paths, match conditions, and source. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ToolsSetRequest +public sealed class PermissionsConfigureAdditionalContentExclusionPolicyRule { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Conditions of which at least one must match. + [JsonPropertyName("ifAnyMatch")] + public IList? IfAnyMatch { get; set; } - /// Complete replacement list for the calling connection. - [JsonPropertyName("tools")] - public IList Tools { get => field ??= []; set; } -} + /// Conditions none of which may match. + [JsonPropertyName("ifNoneMatch")] + public IList? IfNoneMatch { get; set; } -/// Empty result after applying subagent settings. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ToolsUpdateSubagentSettingsResult -{ + /// Path patterns covered by this rule. + [JsonPropertyName("paths")] + public IList Paths { get => field ??= []; set; } + + /// Source descriptor for a `session.permissions.configure` content-exclusion rule, with source name and type. + [JsonPropertyName("source")] + public PermissionsConfigureAdditionalContentExclusionPolicyRuleSource Source { get => field ??= new(); set; } } -/// Subagent model, reasoning effort, context tier, and auto-invocation settings. +/// Content-exclusion policy supplied to `session.permissions.configure`, with rules, last-updated data, and scope. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SubagentSettingsEntry +public sealed class PermissionsConfigureAdditionalContentExclusionPolicy { - /// Whether this agent's runtime-defined proactive invocation prompting is enabled, if supported. Currently consumed by the built-in rubber-duck agent. - [JsonPropertyName("autoInvoke")] - public bool? AutoInvoke { get; set; } - - /// Context tier override for matching subagents. - [JsonPropertyName("contextTier")] - public SubagentSettingsEntryContextTier? ContextTier { get; set; } - - /// Reasoning effort override for matching subagents. - [JsonPropertyName("effortLevel")] - public string? EffortLevel { get; set; } + /// Opaque policy update timestamp supplied by the host. + [JsonPropertyName("last_updated_at")] + public JsonElement LastUpdatedAt { get; set; } - /// Model override for matching subagents. - [JsonPropertyName("model")] - public string? Model { get; set; } + /// Content-exclusion rules to apply. + [JsonPropertyName("rules")] + public IList Rules { get => field ??= []; set; } - /// Whether the configured model strategy is preferred or required. - [JsonPropertyName("modelPolicy")] - public AgentModelPolicy? ModelPolicy { get; set; } + /// Allowed values for the `PermissionsConfigureAdditionalContentExclusionPolicyScope` enumeration. + [JsonPropertyName("scope")] + public PermissionsConfigureAdditionalContentExclusionPolicyScope Scope { get; set; } } -/// Configured per-agent subagent overrides. -public sealed class UpdateSubagentSettingsRequestSubagents +/// If specified, replaces the session's path-permission policy. The runtime constructs the appropriate PathManager based on these inputs (rooted at the session's working directory). Omit to leave the current path policy unchanged. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PermissionPathsConfig { - /// Per-agent settings keyed by subagent agent_type. - [JsonPropertyName("agents")] - public IDictionary? Agents { get; set; } + /// Additional directories to allow tool access to (in addition to the session's working directory). Conventional `.github/skills/` and `.github/agents/` definitions under them also join the session catalogs when their subsystem gates are enabled, so supplying a directory is a trust decision for configuration stored there. When `unrestricted` is true, these are still pre-populated on the UnrestrictedPathManager so they remain visible via getDirectories() (e.g. for @-mention completion). + [JsonPropertyName("additionalDirectories")] + public IList? AdditionalDirectories { get; set; } - /// Names of subagents the user has turned off; they cannot be dispatched. - [JsonPropertyName("disabledSubagents")] - public IList? DisabledSubagents { get; set; } + /// Whether to include the system temp directory in the allowed list (defaults to true). Ignored when `unrestricted` is true. + [JsonPropertyName("includeTempDirectory")] + public bool? IncludeTempDirectory { get; set; } - /// Maximum number of subagents that can run concurrently; applies to usage-based billing users only. - [JsonPropertyName("maxConcurrency")] - public int? MaxConcurrency { get; set; } + /// If true, the runtime allows access to all paths without prompting. Equivalent to constructing an UnrestrictedPathManager. + [JsonPropertyName("unrestricted")] + public bool? Unrestricted { get; set; } - /// Maximum subagent nesting depth; applies to usage-based billing users only. - [JsonPropertyName("maxDepth")] - public int? MaxDepth { get; set; } + /// Workspace root path (special-cased to be allowed even before the directory exists). Ignored when `unrestricted` is true. + [JsonPropertyName("workspacePath")] + public string? WorkspacePath { get; set; } } -/// Subagent settings to apply to the current session. +/// If specified, replaces the session's approved/denied permission rules. Omit to leave the current rules unchanged. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UpdateSubagentSettingsRequest +public sealed class PermissionRulesSet { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Rules that auto-approve matching requests. + [JsonPropertyName("approved")] + public IList Approved { get => field ??= []; set; } - /// Subagent settings to apply, or null to clear the live session override. - [JsonPropertyName("subagents")] - public UpdateSubagentSettingsRequestSubagents? Subagents { get; set; } + /// Rules that auto-deny matching requests. + [JsonPropertyName("denied")] + public IList Denied { get => field ??= []; set; } } -/// RPC data type for SessionCommandsList operations. +/// If specified, replaces the session's URL-permission policy. The runtime constructs a fresh DefaultUrlManager based on these inputs. Omit to leave the current URL policy unchanged. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionCommandsListRequest +public sealed class PermissionUrlsConfig { - /// Include runtime built-in commands. - [JsonPropertyName("includeBuiltins")] - public bool? IncludeBuiltins { get; set; } - - /// Include commands registered by protocol clients, including SDK clients and extensions. - [JsonPropertyName("includeClientCommands")] - public bool? IncludeClientCommands { get; set; } + /// Initial list of allowed URL/domain patterns. Patterns may include path components. Ignored when `unrestricted` is true. + [JsonPropertyName("initialAllowed")] + public IList? InitialAllowed { get; set; } - /// Include enabled user-invocable skills and commands. - [JsonPropertyName("includeSkills")] - public bool? IncludeSkills { get; set; } + /// If true, the runtime allows access to all URLs without prompting. Initial allow-list is ignored when this is true. + [JsonPropertyName("unrestricted")] + public bool? Unrestricted { get; set; } } -/// RPC data type for SessionCommandsListRequestWithSession operations. +/// Patch of permission policy fields to apply (omit a field to leave it unchanged). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionCommandsListRequestWithSession +internal sealed class PermissionsConfigureParams { - /// Include runtime built-in commands. - [JsonPropertyName("includeBuiltins")] - public bool? IncludeBuiltins { get; set; } + /// If specified, replaces the host-supplied GitHub Content Exclusion policies on the session (combined with natively-discovered policies when evaluating tool/file access). Omit to leave the current policies unchanged. + [JsonPropertyName("additionalContentExclusionPolicies")] + public IList? AdditionalContentExclusionPolicies { get; set; } - /// Include commands registered by protocol clients, including SDK clients and extensions. - [JsonPropertyName("includeClientCommands")] - public bool? IncludeClientCommands { get; set; } + /// If specified, sets whether path/URL read permission requests are auto-approved. Omit to leave the current value unchanged. + [JsonPropertyName("approveAllReadPermissionRequests")] + public bool? ApproveAllReadPermissionRequests { get; set; } - /// Include enabled user-invocable skills and commands. - [JsonPropertyName("includeSkills")] - public bool? IncludeSkills { get; set; } + /// If specified, sets whether tool permission requests are auto-approved without prompting. Omit to leave the current value unchanged. + [JsonPropertyName("approveAllToolPermissionRequests")] + public bool? ApproveAllToolPermissionRequests { get; set; } + + /// If specified, replaces the session's path-permission policy. The runtime constructs the appropriate PathManager based on these inputs (rooted at the session's working directory). Omit to leave the current path policy unchanged. + [JsonPropertyName("paths")] + public PermissionPathsConfig? Paths { get; set; } + + /// If specified, replaces the session's approved/denied permission rules. Omit to leave the current rules unchanged. + [JsonPropertyName("rules")] + public PermissionRulesSet? Rules { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// If specified, replaces the session's URL-permission policy. The runtime constructs a fresh DefaultUrlManager based on these inputs. Omit to leave the current URL policy unchanged. + [JsonPropertyName("urls")] + public PermissionUrlsConfig? Urls { get; set; } } -/// Result of invoking the slash command (text output, prompt to send to the agent, completion, or subcommand selection). +/// Indicates whether the permission decision was applied; false when the request was already resolved. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PermissionRequestResult +{ + /// Whether the permission request was handled successfully. + [JsonPropertyName("success")] + public bool Success { get; set; } +} + +/// The client's response to the pending permission prompt. /// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonPolymorphic( TypeDiscriminatorPropertyName = "kind", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(SlashCommandInvocationResultText), "text")] -[JsonDerivedType(typeof(SlashCommandInvocationResultAgentPrompt), "agent-prompt")] -[JsonDerivedType(typeof(SlashCommandInvocationResultCompleted), "completed")] -[JsonDerivedType(typeof(SlashCommandInvocationResultSelectSubcommand), "select-subcommand")] -[JsonDerivedType(typeof(SlashCommandInvocationResultAddTimelineEntry), "add-timeline-entry")] -[JsonDerivedType(typeof(SlashCommandInvocationResultShowDialog), "show-dialog")] -[JsonDerivedType(typeof(SlashCommandInvocationResultSetModel), "set-model")] -[JsonDerivedType(typeof(SlashCommandInvocationResultSetPlanModel), "set-plan-model")] -public partial class SlashCommandInvocationResult +[JsonDerivedType(typeof(PermissionDecisionApproveOnce), "approve-once")] +[JsonDerivedType(typeof(PermissionDecisionApproveForSession), "approve-for-session")] +[JsonDerivedType(typeof(PermissionDecisionApproveReadOnlyForSession), "approve-read-only-for-session")] +[JsonDerivedType(typeof(PermissionDecisionApproveForLocation), "approve-for-location")] +[JsonDerivedType(typeof(PermissionDecisionApprovePermanently), "approve-permanently")] +[JsonDerivedType(typeof(PermissionDecisionReject), "reject")] +[JsonDerivedType(typeof(PermissionDecisionUserNotAvailable), "user-not-available")] +[JsonDerivedType(typeof(PermissionDecisionApproved), "approved")] +[JsonDerivedType(typeof(PermissionDecisionApprovedForSession), "approved-for-session")] +[JsonDerivedType(typeof(PermissionDecisionApprovedForLocation), "approved-for-location")] +[JsonDerivedType(typeof(PermissionDecisionCancelled), "cancelled")] +[JsonDerivedType(typeof(PermissionDecisionDeniedByRules), "denied-by-rules")] +[JsonDerivedType(typeof(PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser), "denied-no-approval-rule-and-could-not-request-from-user")] +[JsonDerivedType(typeof(PermissionDecisionDeniedInteractivelyByUser), "denied-interactively-by-user")] +[JsonDerivedType(typeof(PermissionDecisionDeniedByContentExclusionPolicy), "denied-by-content-exclusion-policy")] +[JsonDerivedType(typeof(PermissionDecisionDeniedByPermissionRequestHook), "denied-by-permission-request-hook")] +public partial class PermissionDecision { /// The type discriminator. [JsonPropertyName("kind")] @@ -21649,822 +24843,767 @@ public partial class SlashCommandInvocationResult } -/// Slash-command invocation result containing text output plus Markdown/ANSI rendering flags. -/// The text variant of . +/// Permission-decision request variant to approve only the current permission request. +/// The approve-once variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SlashCommandInvocationResultText : SlashCommandInvocationResult +public partial class PermissionDecisionApproveOnce : PermissionDecision { /// [JsonIgnore] - public override string Kind => "text"; + public override string Kind => "approve-once"; - /// Whether text contains Markdown. + /// True only when a host surfaced this request to a user who approved it. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("markdown")] - public bool? Markdown { get; set; } + [JsonPropertyName("approvedInteractively")] + public bool? ApprovedInteractively { get; set; } +} - /// Whether ANSI sequences should be preserved. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("preserveAnsi")] - public bool? PreserveAnsi { get; set; } +/// Session-scoped approval to remember (tool prompts only; omitted for path/url prompts). +/// Polymorphic base type discriminated by kind. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalCommands), "commands")] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalRead), "read")] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalWrite), "write")] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalMcp), "mcp")] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalMcpSampling), "mcp-sampling")] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalMemory), "memory")] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalCustomTool), "custom-tool")] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalExtensionManagement), "extension-management")] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalWorkflow), "workflow")] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess), "extension-permission-access")] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalExtensionEnvAccess), "extension-env-access")] +public partial class PermissionDecisionApproveForSessionApproval +{ + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// True when the invocation mutated user runtime settings; consumers caching settings should refresh. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("runtimeSettingsChanged")] - public bool? RuntimeSettingsChanged { get; set; } - /// Present when the invocation changed the sandbox for this session only. Nothing was persisted, so consumers must mirror the change onto the live session rather than reloading settings, and must not treat it as a settings change. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("sandboxSessionChange")] - public SandboxSessionChange? SandboxSessionChange { get; set; } +/// Session-scoped approval details for specific command identifiers. +/// The commands variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class PermissionDecisionApproveForSessionApprovalCommands : PermissionDecisionApproveForSessionApproval +{ + /// + [JsonIgnore] + public override string Kind => "commands"; - /// Text output for the client to render. - [JsonPropertyName("text")] - public required string Text { get; set; } + /// Command identifiers covered by this approval. + [JsonPropertyName("commandIdentifiers")] + public required IList CommandIdentifiers { get; set; } } -/// Slash-command invocation result that submits an agent prompt, with display prompt, optional mode, optional user-facing notice, and settings-change flag. -/// The agent-prompt variant of . +/// Session-scoped approval details for read-only filesystem operations. +/// The read variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SlashCommandInvocationResultAgentPrompt : SlashCommandInvocationResult +public partial class PermissionDecisionApproveForSessionApprovalRead : PermissionDecisionApproveForSessionApproval { /// [JsonIgnore] - public override string Kind => "agent-prompt"; - - /// Prompt text to display to the user. - [JsonPropertyName("displayPrompt")] - public required string DisplayPrompt { get; set; } - - /// Optional target session mode for the agent prompt. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("mode")] - public SessionMode? Mode { get; set; } - - /// Optional user-facing notice to show before the prompt is submitted. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("notice")] - public string? Notice { get; set; } - - /// Prompt to submit to the agent. - [JsonPropertyName("prompt")] - public required string Prompt { get; set; } - - /// True when the invocation mutated user runtime settings; consumers caching settings should refresh. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("runtimeSettingsChanged")] - public bool? RuntimeSettingsChanged { get; set; } + public override string Kind => "read"; } -/// Slash-command invocation result indicating completion, with optional message and settings-change flag. -/// The completed variant of . +/// Session-scoped approval details for filesystem write operations. +/// The write variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SlashCommandInvocationResultCompleted : SlashCommandInvocationResult +public partial class PermissionDecisionApproveForSessionApprovalWrite : PermissionDecisionApproveForSessionApproval { /// [JsonIgnore] - public override string Kind => "completed"; - - /// Optional user-facing message describing the completed command. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("message")] - public string? Message { get; set; } - - /// Optional target session mode applied without submitting an agent prompt. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("mode")] - public SessionMode? Mode { get; set; } - - /// True when the invocation mutated user runtime settings; consumers caching settings should refresh. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("runtimeSettingsChanged")] - public bool? RuntimeSettingsChanged { get; set; } + public override string Kind => "write"; } -/// Selectable slash-command subcommand option with name, description, and optional group label. +/// Session-scoped approval details for an MCP server tool, or all tools on the server when `toolName` is null. +/// The mcp variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SlashCommandSelectSubcommandOption +public partial class PermissionDecisionApproveForSessionApprovalMcp : PermissionDecisionApproveForSessionApproval { - /// Human-readable description of the subcommand. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "mcp"; - /// Optional group label for organizing options. - [JsonPropertyName("group")] - public string? Group { get; set; } + /// MCP server name. + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } - /// Subcommand name to invoke. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// MCP tool name, or null to cover every tool on the server. + [JsonPropertyName("toolName")] + public string? ToolName { get; set; } } -/// Slash-command invocation result asking the client to present subcommand options for a parent command. -/// The select-subcommand variant of . +/// Session-scoped approval details for MCP sampling requests from a server. +/// The mcp-sampling variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SlashCommandInvocationResultSelectSubcommand : SlashCommandInvocationResult +public partial class PermissionDecisionApproveForSessionApprovalMcpSampling : PermissionDecisionApproveForSessionApproval { /// [JsonIgnore] - public override string Kind => "select-subcommand"; - - /// Parent command name that requires subcommand selection. - [JsonPropertyName("command")] - public required string Command { get; set; } - - /// Available subcommand options for the client to present. - [JsonPropertyName("options")] - public required IList Options { get; set; } - - /// True when the invocation mutated user runtime settings; consumers caching settings should refresh. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("runtimeSettingsChanged")] - public bool? RuntimeSettingsChanged { get; set; } + public override string Kind => "mcp-sampling"; - /// Human-readable title for the selection UI. - [JsonPropertyName("title")] - public required string Title { get; set; } + /// MCP server name. + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } } -/// RPC data type for SlashCommandTimelineEntry operations. +/// Session-scoped approval details for writes to long-term memory. +/// The memory variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SlashCommandTimelineEntry +public partial class PermissionDecisionApproveForSessionApprovalMemory : PermissionDecisionApproveForSessionApproval { - /// What the user must do to recover, when the entry reports a failure the runtime knows an action for. The `text` never names a client affordance, so a client that offers one renders it from this value. - [JsonPropertyName("remediation")] - public RemediationAction? Remediation { get; set; } - - /// Text displayed for the timeline entry. - [JsonPropertyName("text")] - public string Text { get; set; } = string.Empty; - - /// Timeline entry presentation type. - [JsonPropertyName("type")] - public string Type { get; set; } = string.Empty; - - /// Optional URL associated with the timeline entry. - [JsonPropertyName("url")] - public string? Url { get; set; } + /// + [JsonIgnore] + public override string Kind => "memory"; } -/// The add-timeline-entry variant of . +/// Session-scoped approval details for a custom tool, keyed by tool name. +/// The custom-tool variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SlashCommandInvocationResultAddTimelineEntry : SlashCommandInvocationResult +public partial class PermissionDecisionApproveForSessionApprovalCustomTool : PermissionDecisionApproveForSessionApproval { /// [JsonIgnore] - public override string Kind => "add-timeline-entry"; - - /// Timeline entry the host should append. - [JsonPropertyName("entry")] - public required SlashCommandTimelineEntry Entry { get; set; } - - /// Optional text the host should prefill into the input editor. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("prefillInput")] - public string? PrefillInput { get; set; } + public override string Kind => "custom-tool"; - /// Whether command execution changed persisted runtime settings. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("runtimeSettingsChanged")] - public bool? RuntimeSettingsChanged { get; set; } + /// Custom tool name. + [JsonPropertyName("toolName")] + public required string ToolName { get; set; } } -/// RPC data type for SlashCommandModelPickerDialog operations. +/// Session-scoped approval details for extension-management operations, optionally narrowed by operation. +/// The extension-management variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SlashCommandModelPickerDialog +public partial class PermissionDecisionApproveForSessionApprovalExtensionManagement : PermissionDecisionApproveForSessionApproval { - /// Discriminator for a model-picker dialog. - [JsonPropertyName("kind")] - public string Kind { get; set; } = string.Empty; - - /// Model that should be enabled before it can be selected. - [JsonPropertyName("modelToEnable")] - public string? ModelToEnable { get; set; } - - /// Settings scope the picker should modify. - [JsonPropertyName("scope")] - public string? Scope { get; set; } + /// + [JsonIgnore] + public override string Kind => "extension-management"; - /// Model-selection target represented by the picker. - [JsonPropertyName("target")] - public string? Target { get; set; } + /// Optional operation identifier; when omitted, the approval covers all extension management operations. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("operation")] + public string? Operation { get; set; } } -/// The show-dialog variant of . +/// Session-scoped workflow approval, optionally narrowed by approval key. +/// The workflow variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SlashCommandInvocationResultShowDialog : SlashCommandInvocationResult +public partial class PermissionDecisionApproveForSessionApprovalWorkflow : PermissionDecisionApproveForSessionApproval { /// [JsonIgnore] - public override string Kind => "show-dialog"; - - /// Dialog the host should display. - [JsonPropertyName("dialog")] - public required SlashCommandModelPickerDialog Dialog { get; set; } + public override string Kind => "workflow"; - /// Whether command execution changed persisted runtime settings. + /// Optional workflow operation name or canonical approval key; when omitted, the approval covers all workflow operations. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("runtimeSettingsChanged")] - public bool? RuntimeSettingsChanged { get; set; } + [JsonPropertyName("approvalKey")] + public string? ApprovalKey { get; set; } } -/// User-settings snapshot to restore if the host cancels the model switch. -public sealed class SlashCommandInvocationResultSetModelRevertOnCancel +/// Session-scoped approval details for an extension's permission-gated capability access, keyed by extension name. +/// The extension-permission-access variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess : PermissionDecisionApproveForSessionApproval { + /// + [JsonIgnore] + public override string Kind => "extension-permission-access"; + + /// Extension name. + [JsonPropertyName("extensionName")] + public required string ExtensionName { get; set; } } -/// The set-model variant of . +/// Session-scoped approval details for an extension's access to sensitive environment variables, keyed by extension name and the exact set of variable names. +/// The extension-env-access variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SlashCommandInvocationResultSetModel : SlashCommandInvocationResult +public partial class PermissionDecisionApproveForSessionApprovalExtensionEnvAccess : PermissionDecisionApproveForSessionApproval { /// [JsonIgnore] - public override string Kind => "set-model"; - - /// Auto routing profile selected by the command, when the model is Auto. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("autoTier")] - public AutoTier? AutoTier { get; set; } - - /// Model selected by the command. - [JsonPropertyName("model")] - public required string Model { get; set; } - - /// Reasoning effort selected for the model. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("reasoningEffort")] - public string? ReasoningEffort { get; set; } - - /// Repository settings scope modified by the command. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("repoScope")] - public string? RepoScope { get; set; } - - /// User-settings snapshot to restore if the host cancels the model switch. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("revertOnCancel")] - public SlashCommandInvocationResultSetModelRevertOnCancel? RevertOnCancel { get; set; } - - /// Whether command execution changed persisted runtime settings. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("runtimeSettingsChanged")] - public bool? RuntimeSettingsChanged { get; set; } + public override string Kind => "extension-env-access"; - /// Settings scope modified by the command. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("scope")] - public string? Scope { get; set; } + /// Names of the sensitive environment variables this approval covers. Values are never persisted. + [JsonPropertyName("environmentVariables")] + public required IList EnvironmentVariables { get; set; } - /// User-facing warning produced while selecting the model. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("warning")] - public string? Warning { get; set; } + /// Extension name. + [JsonPropertyName("extensionName")] + public required string ExtensionName { get; set; } } -/// The set-plan-model variant of . +/// Permission-decision request variant to approve for the rest of the session, with optional tool approval or URL domain. +/// The approve-for-session variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SlashCommandInvocationResultSetPlanModel : SlashCommandInvocationResult +public partial class PermissionDecisionApproveForSession : PermissionDecision { /// [JsonIgnore] - public override string Kind => "set-plan-model"; - - /// User-facing confirmation message for the plan-model selection. - [JsonPropertyName("message")] - public required string Message { get; set; } + public override string Kind => "approve-for-session"; - /// Dedicated model selected for plan mode. + /// Session-scoped approval to remember (tool prompts only; omitted for path/url prompts). [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("planModel")] - public string? PlanModel { get; set; } + [JsonPropertyName("approval")] + public PermissionDecisionApproveForSessionApproval? Approval { get; set; } - /// Whether command execution changed persisted runtime settings. + /// URL domain to approve for the rest of the session (URL prompts only). [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("runtimeSettingsChanged")] - public bool? RuntimeSettingsChanged { get; set; } + [JsonPropertyName("domain")] + public string? Domain { get; set; } } -/// Slash command name and optional raw input string to invoke. +/// Approve file-tool read access to specific directories for this logical session, including continuation or resume. +/// The approve-read-only-for-session variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CommandsInvokeRequest +public partial class PermissionDecisionApproveReadOnlyForSession : PermissionDecision { - /// Raw input after the command name. - [JsonPropertyName("input")] - public string? Input { get; set; } - - /// Command name. Leading slashes are stripped and the name is matched case-insensitively. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - /// Optional client surface that initiated the invocation. - [JsonPropertyName("origin")] - public CommandsInvocationOrigin? Origin { get; set; } + /// + [JsonIgnore] + public override string Kind => "approve-read-only-for-session"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Canonical directories covered by the read-only grant. + [JsonPropertyName("directories")] + public required IList Directories { get; set; } } -/// Whether finalizing the invocation effect succeeded, and the failure reason when it did not. +/// Approval to persist for this location. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CommandsFinalizeInvocationEffectResult +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalCommands), "commands")] +[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalRead), "read")] +[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalWrite), "write")] +[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalMcp), "mcp")] +[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalMcpSampling), "mcp-sampling")] +[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalMemory), "memory")] +[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalCustomTool), "custom-tool")] +[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalExtensionManagement), "extension-management")] +[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalWorkflow), "workflow")] +[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess), "extension-permission-access")] +[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalExtensionEnvAccess), "extension-env-access")] +public partial class PermissionDecisionApproveForLocationApproval { - /// Failure reason when the invocation effect could not be finalized. - [JsonPropertyName("error")] - public string? Error { get; set; } - - /// Whether the pending invocation effect was finalized successfully. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// The slash-command result object that produced the pending effect, echoed back unchanged. -public sealed class CommandsFinalizeInvocationEffectRequestEffect -{ -} -/// The pending slash-command invocation effect to finalize, plus whether the host applied or cancelled it. +/// Location-scoped approval details for specific command identifiers. +/// The commands variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CommandsFinalizeInvocationEffectRequest +public partial class PermissionDecisionApproveForLocationApprovalCommands : PermissionDecisionApproveForLocationApproval { - /// The slash-command result object that produced the pending effect, echoed back unchanged. - [JsonPropertyName("effect")] - public CommandsFinalizeInvocationEffectRequestEffect Effect { get => field ??= new(); set; } - - /// Whether the host applied or cancelled the pending invocation effect. - [JsonPropertyName("outcome")] - public CommandsInvocationEffectOutcome Outcome { get; set; } - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// + [JsonIgnore] + public override string Kind => "commands"; -/// Indicates whether the pending client-handled command was completed successfully. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CommandsHandlePendingCommandResult -{ - /// Whether the command was handled successfully. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Command identifiers covered by this approval. + [JsonPropertyName("commandIdentifiers")] + public required IList CommandIdentifiers { get; set; } } -/// Pending command request ID and an optional error if the client handler failed. +/// Location-scoped approval details for read-only filesystem operations. +/// The read variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CommandsHandlePendingCommandRequest +public partial class PermissionDecisionApproveForLocationApprovalRead : PermissionDecisionApproveForLocationApproval { - /// Error message if the command handler failed. - [JsonPropertyName("error")] - public string? Error { get; set; } - - /// Request ID from the command invocation event. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "read"; } -/// Error message produced while executing the command, if any. +/// Location-scoped approval details for filesystem write operations. +/// The write variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ExecuteCommandResult +public partial class PermissionDecisionApproveForLocationApprovalWrite : PermissionDecisionApproveForLocationApproval { - /// Error message produced while executing the command, if any. Omitted when the handler succeeded. - [JsonPropertyName("error")] - public string? Error { get; set; } + /// + [JsonIgnore] + public override string Kind => "write"; } -/// Slash command name and argument string to execute synchronously. +/// Location-scoped approval details for an MCP server tool, or all tools on the server when `toolName` is null. +/// The mcp variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ExecuteCommandParams +public partial class PermissionDecisionApproveForLocationApprovalMcp : PermissionDecisionApproveForLocationApproval { - /// Argument string to pass to the command (empty string if none). - [JsonPropertyName("args")] - public string Args { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "mcp"; - /// Name of the slash command to invoke (without the leading '/'). - [JsonPropertyName("commandName")] - public string CommandName { get; set; } = string.Empty; + /// MCP server name. + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// MCP tool name, or null to cover every tool on the server. + [JsonPropertyName("toolName")] + public string? ToolName { get; set; } } -/// Indicates whether the command was accepted into the local execution queue. -/// Data type discriminated by queued. +/// Location-scoped approval details for MCP sampling requests from a server. +/// The mcp-sampling variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class EnqueueCommandResult +public partial class PermissionDecisionApproveForLocationApprovalMcpSampling : PermissionDecisionApproveForLocationApproval { - /// The boolean discriminator. - [JsonPropertyName("queued")] - public bool Queued { get; set; } + /// + [JsonIgnore] + public override string Kind => "mcp-sampling"; - /// Stable opaque ID of the queued command. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("queueId")] - public string? QueueId { get; set; } + /// MCP server name. + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } } -/// Slash-prefixed command string to enqueue for FIFO processing. +/// Location-scoped approval details for writes to long-term memory. +/// The memory variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class EnqueueCommandParams +public partial class PermissionDecisionApproveForLocationApprovalMemory : PermissionDecisionApproveForLocationApproval { - /// Slash-prefixed command string to enqueue, e.g. '/compact' or '/model gpt-4'. Queued FIFO with any in-flight items; if the session is idle, processing kicks off immediately. - [JsonPropertyName("command")] - public string Command { get; set; } = string.Empty; - - /// Optional user-facing text for the queue row. The command string is shown when omitted. - [JsonPropertyName("displayText")] - public string? DisplayText { get; set; } - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "memory"; } -/// Indicates whether the queued-command response was matched to a pending request. +/// Location-scoped approval details for a custom tool, keyed by tool name. +/// The custom-tool variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CommandsRespondToQueuedCommandResult +public partial class PermissionDecisionApproveForLocationApprovalCustomTool : PermissionDecisionApproveForLocationApproval { - /// Whether a pending queued command with the given request ID was found and resolved. False when the request was already resolved, cancelled, or unknown. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// + [JsonIgnore] + public override string Kind => "custom-tool"; + + /// Custom tool name. + [JsonPropertyName("toolName")] + public required string ToolName { get; set; } } -/// Result of the queued command execution. -/// Data type discriminated by handled. +/// Location-scoped approval details for extension-management operations, optionally narrowed by operation. +/// The extension-management variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class QueuedCommandResult +public partial class PermissionDecisionApproveForLocationApprovalExtensionManagement : PermissionDecisionApproveForLocationApproval { - /// The boolean discriminator. - [JsonPropertyName("handled")] - public bool Handled { get; set; } + /// + [JsonIgnore] + public override string Kind => "extension-management"; - /// When true, the runtime will not process subsequent queued commands until a new request comes in. + /// Optional operation identifier; when omitted, the approval covers all extension management operations. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("stopProcessingQueue")] - public bool? StopProcessingQueue { get; set; } + [JsonPropertyName("operation")] + public string? Operation { get; set; } } -/// Queued-command request ID and the result indicating whether the host executed it (and whether to stop processing further queued commands). +/// Location-scoped workflow approval, optionally narrowed by approval key. +/// The workflow variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CommandsRespondToQueuedCommandRequest +public partial class PermissionDecisionApproveForLocationApprovalWorkflow : PermissionDecisionApproveForLocationApproval { - /// Request ID from the `command.queued` event the host is responding to. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; - - /// Result of the queued command execution. - [JsonPropertyName("result")] - public QueuedCommandResult Result { get => field ??= new(); set; } + /// + [JsonIgnore] + public override string Kind => "workflow"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Optional workflow operation name or canonical approval key; when omitted, the approval covers all workflow operations. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("approvalKey")] + public string? ApprovalKey { get; set; } } -/// Telemetry engagement ID for the session, when available. +/// Location-scoped approval details for an extension's permission-gated capability access, keyed by extension name. +/// The extension-permission-access variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionTelemetryEngagement +public partial class PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess : PermissionDecisionApproveForLocationApproval { - /// Current telemetry engagement ID, when available. - [JsonPropertyName("engagementId")] - public string? EngagementId { get; set; } -} + /// + [JsonIgnore] + public override string Kind => "extension-permission-access"; -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionTelemetryGetEngagementIdRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Extension name. + [JsonPropertyName("extensionName")] + public required string ExtensionName { get; set; } } -/// Feature override key/value pairs to attach to subsequent telemetry events from this session. +/// Location-scoped approval details for an extension's access to sensitive environment variables, keyed by extension name and the exact set of variable names. +/// The extension-env-access variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TelemetrySetFeatureOverridesRequest +public partial class PermissionDecisionApproveForLocationApprovalExtensionEnvAccess : PermissionDecisionApproveForLocationApproval { - /// Override key/value pairs to attach to subsequent telemetry events from this session. Replaces any previously-set overrides. - [JsonPropertyName("features")] - public IDictionary Features { get => field ??= new Dictionary(); set; } + /// + [JsonIgnore] + public override string Kind => "extension-env-access"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Names of the sensitive environment variables this approval covers. Values are never persisted. + [JsonPropertyName("environmentVariables")] + public required IList EnvironmentVariables { get; set; } + + /// Extension name. + [JsonPropertyName("extensionName")] + public required string ExtensionName { get; set; } } -/// Completed transient query. Ordered chunks and the terminal outcome are also delivered through `ui.ephemeral_query` session events while it runs. +/// Permission-decision request variant to approve and persist a permission for a project location, with approval details and location key. +/// The approve-for-location variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIEphemeralQueryResult +public partial class PermissionDecisionApproveForLocation : PermissionDecision { - /// Answer returned by the model. - [JsonPropertyName("answer")] - public string Answer { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "approve-for-location"; + + /// Approval to persist for this location. + [JsonPropertyName("approval")] + public required PermissionDecisionApproveForLocationApproval Approval { get; set; } + + /// Location key (git root or cwd) to persist the approval to. + [JsonPropertyName("locationKey")] + public required string LocationKey { get; set; } } -/// Transient question to answer without adding it to conversation history. +/// Permission-decision request variant to permanently approve a URL domain across sessions. +/// The approve-permanently variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIEphemeralQueryRequest +public partial class PermissionDecisionApprovePermanently : PermissionDecision { - /// Question to answer from the current conversation context. - [JsonPropertyName("question")] - public string Question { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "approve-permanently"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// URL domain to approve permanently. + [JsonPropertyName("domain")] + public required string Domain { get; set; } } -/// The elicitation response (accept with form values, decline, or cancel). +/// Permission-decision request variant to reject a pending permission request, with optional feedback. +/// The reject variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIElicitationResponse +public partial class PermissionDecisionReject : PermissionDecision { - /// MCP response metadata. - [JsonPropertyName("_meta")] - public IDictionary? Meta { get; set; } - - /// The user's response: accept (submitted), decline (rejected), or cancel (dismissed). - [JsonPropertyName("action")] - public UIElicitationResponseAction Action { get; set; } + /// + [JsonIgnore] + public override string Kind => "reject"; - /// The form values submitted by the user (present when action is 'accept'). - [JsonPropertyName("content")] - public IDictionary? Content { get; set; } + /// Optional feedback explaining the rejection. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("feedback")] + public string? Feedback { get; set; } } -/// JSON Schema describing the form fields to present to the user. +/// Permission-decision variant indicating no user was available to confirm the request. +/// The user-not-available variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIElicitationSchema +public partial class PermissionDecisionUserNotAvailable : PermissionDecision { - /// Form field definitions, keyed by field name. - [JsonPropertyName("properties")] - public IDictionary Properties { get => field ??= new Dictionary(); set; } - - /// List of required field names. - [JsonPropertyName("required")] - public IList? Required { get; set; } - - /// Schema type indicator (always 'object'). - [JsonPropertyName("type")] - public string Type { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "user-not-available"; } -/// Metadata controlling an MCP task's lifetime. +/// Permission-decision variant indicating the request was approved. +/// The approved variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpTaskMetadata +public partial class PermissionDecisionApproved : PermissionDecision { - /// Task time-to-live. - [JsonPropertyName("ttl")] - public long? Ttl { get; set; } + /// + [JsonIgnore] + public override string Kind => "approved"; } -/// Prompt message and JSON schema describing the form fields to elicit from the user. +/// Permission-decision variant indicating approval was remembered for the session, with approval details. +/// The approved-for-session variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIElicitationRequest +public partial class PermissionDecisionApprovedForSession : PermissionDecision { - /// MCP request metadata. - [JsonPropertyName("_meta")] - public IDictionary? Meta { get; set; } - - /// Message describing what information is needed from the user. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "approved-for-session"; - /// Elicitation mode. Omitted and form are equivalent for structured elicitation. - [JsonPropertyName("mode")] - public McpElicitationFormMode? Mode { get; set; } + /// The approval to add as a session-scoped rule. + [JsonPropertyName("approval")] + public required UserToolSessionApproval Approval { get; set; } +} - /// JSON Schema describing the form fields to present to the user. - [JsonPropertyName("requestedSchema")] - public UIElicitationSchema RequestedSchema { get => field ??= new(); set; } +/// Permission-decision variant indicating approval was persisted for a project location, with approval details and location key. +/// The approved-for-location variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class PermissionDecisionApprovedForLocation : PermissionDecision +{ + /// + [JsonIgnore] + public override string Kind => "approved-for-location"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// The approval to persist for this location. + [JsonPropertyName("approval")] + public required UserToolSessionApproval Approval { get; set; } - /// MCP task metadata. - [JsonPropertyName("task")] - public McpTaskMetadata? Task { get; set; } + /// The location key (git root or cwd) to persist the approval to. + [JsonPropertyName("locationKey")] + public required string LocationKey { get; set; } } -/// Indicates whether the elicitation response was accepted; false if it was already resolved by another client. +/// Permission-decision variant indicating the request was cancelled before use, with an optional reason. +/// The cancelled variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIElicitationResult +public partial class PermissionDecisionCancelled : PermissionDecision { - /// Whether the response was accepted. False if the request was already resolved by another client. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// + [JsonIgnore] + public override string Kind => "cancelled"; + + /// Optional explanation of why the request was cancelled. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("reason")] + public string? Reason { get; set; } } -/// Pending elicitation request ID and the user's response (accept/decline/cancel + form values). +/// Permission-decision variant indicating explicit denial by permission rules, with the matching rules. +/// The denied-by-rules variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIHandlePendingElicitationRequest +public partial class PermissionDecisionDeniedByRules : PermissionDecision { - /// The unique request ID from the elicitation.requested event. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; - - /// The elicitation response (accept with form values, decline, or cancel). - [JsonPropertyName("result")] - public UIElicitationResponse Result { get => field ??= new(); set; } + /// + [JsonIgnore] + public override string Kind => "denied-by-rules"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Rules that denied the request. + [JsonPropertyName("rules")] + public required IList Rules { get; set; } } -/// Indicates whether the pending UI request was resolved by this call. +/// Permission-decision variant indicating no approval rule matched and user confirmation was unavailable. +/// The denied-no-approval-rule-and-could-not-request-from-user variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIHandlePendingResult +public partial class PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser : PermissionDecision { - /// True if the request was still pending and was resolved by this call. False if the request ID was unknown, already resolved by another client (e.g. GitHub), expired, or otherwise no longer pending. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// + [JsonIgnore] + public override string Kind => "denied-no-approval-rule-and-could-not-request-from-user"; } -/// User response for a pending user-input request, with answer text and whether it was typed freeform. +/// Permission-decision variant indicating the user denied an interactive prompt, with optional feedback and force-reject flag. +/// The denied-interactively-by-user variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIUserInputResponse +public partial class PermissionDecisionDeniedInteractivelyByUser : PermissionDecision { - /// The user's answer text. - [JsonPropertyName("answer")] - public string Answer { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "denied-interactively-by-user"; - /// True if the user typed a freeform response, false if they selected a presented choice. Used by telemetry to differentiate between free text input and choice selection. - [JsonPropertyName("wasFreeform")] - public bool WasFreeform { get; set; } + /// Optional feedback from the user explaining the denial. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("feedback")] + public string? Feedback { get; set; } + + /// Whether to force-reject the current agent turn. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("forceReject")] + public bool? ForceReject { get; set; } } -/// Request ID of a pending `user_input.requested` event and the user's response. +/// Permission-decision variant indicating denial by content-exclusion policy, with path and message. +/// The denied-by-content-exclusion-policy variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIHandlePendingUserInputRequest +public partial class PermissionDecisionDeniedByContentExclusionPolicy : PermissionDecision { - /// The unique request ID from the user_input.requested event. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "denied-by-content-exclusion-policy"; - /// User response for a pending user-input request, with answer text and whether it was typed freeform. - [JsonPropertyName("response")] - public UIUserInputResponse Response { get => field ??= new(); set; } + /// Human-readable explanation of why the path was excluded. + [JsonPropertyName("message")] + public required string Message { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// File path that triggered the exclusion. + [JsonPropertyName("path")] + public required string Path { get; set; } } -/// Optional sampling result payload. Omit to reject/cancel the sampling request without providing a result. +/// Permission-decision variant indicating denial by a permission request hook, with optional message and interrupt flag. +/// The denied-by-permission-request-hook variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIHandlePendingSamplingResponse +public partial class PermissionDecisionDeniedByPermissionRequestHook : PermissionDecision { + /// + [JsonIgnore] + public override string Kind => "denied-by-permission-request-hook"; + + /// Whether to interrupt the current agent turn. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("interrupt")] + public bool? Interrupt { get; set; } + + /// Optional message from the hook explaining the denial. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("message")] + public string? Message { get; set; } } -/// Request ID of a pending `sampling.requested` event and an optional sampling result payload (omit to reject). +/// Pending permission request ID and the decision to apply (approve/reject and scope). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIHandlePendingSamplingRequest +internal sealed class PermissionDecisionRequest { - /// The unique request ID from the sampling.requested event. + /// Optional informational context describing how and where this response was made. Omit it to preserve legacy behavior without attributing an origin. + [JsonPropertyName("decisionContext")] + public PermissionDecisionContext? DecisionContext { get; set; } + + /// Request ID of the pending permission request. [JsonPropertyName("requestId")] public string RequestId { get; set; } = string.Empty; - /// Optional sampling result payload. Omit to reject/cancel the sampling request without providing a result. - [JsonPropertyName("response")] - public UIHandlePendingSamplingResponse? Response { get; set; } + /// The client's response to the pending permission prompt. + [JsonPropertyName("result")] + public PermissionDecision Result { get => field ??= new(); set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Request ID of a pending `auto_mode_switch.requested` event and the user's response. +/// Pending permission prompt reconstructed from event history, with request ID and user-facing prompt details. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIHandlePendingAutoModeSwitchRequest +public sealed class PendingPermissionRequest { - /// The unique request ID from the auto_mode_switch.requested event. + /// Permission-recovery episode that authorized this request to surface for interactive attention. + [JsonPropertyName("recoveryEpisodeId")] + public string? RecoveryEpisodeId { get; set; } + + /// The user-facing permission prompt details (commands, write, read, mcp, url, memory, custom-tool, path, hook). + [JsonPropertyName("request")] + public PermissionPromptRequest Request { get; set; } = null!; + + /// Unique identifier for the pending permission request. [JsonPropertyName("requestId")] public string RequestId { get; set; } = string.Empty; +} - /// User's choice for auto-mode switching: yes (allow this turn), yes_always (allow + persist as setting), or no (decline). - [JsonPropertyName("response")] - public UIAutoModeSwitchResponse Response { get; set; } +/// List of pending permission requests reconstructed from event history. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PendingPermissionRequestList +{ + /// Pending permission prompts reconstructed from the session's event history. Equivalent to the set of `permission.requested` events that have not yet been followed by a matching `permission.completed` event. Used by clients (e.g. the CLI) to hydrate UI for prompts that were emitted before the client attached to the session. + [JsonPropertyName("items")] + public IList Items { get => field ??= []; set; } +} +/// No parameters; returns currently-pending permission requests for the session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class PermissionsPendingRequestsRequest +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// The user's selected action for an exhausted session limit. +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UISessionLimitsExhaustedResponse +public sealed class PermissionsSetApproveAllResult { - /// Action selected by the user. - [JsonPropertyName("action")] - public UISessionLimitsExhaustedResponseAction Action { get; set; } - - /// AI Credits to add to the current max when action is 'add'. - [JsonPropertyName("additionalAiCredits")] - public double? AdditionalAiCredits { get; set; } - - /// New absolute max AI Credits when action is 'set'. - [JsonPropertyName("maxAiCredits")] - public double? MaxAiCredits { get; set; } + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Request ID of a pending `session_limits_exhausted.requested` event and the user's selected limit action. +/// Allow-all toggle for tool permission requests, with an optional telemetry source. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIHandlePendingSessionLimitsExhaustedRequest +internal sealed class PermissionsSetApproveAllRequest { - /// The unique request ID from the session_limits_exhausted.requested event. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; - - /// The selected session-limit action. - [JsonPropertyName("response")] - public UISessionLimitsExhaustedResponse Response { get => field ??= new(); set; } + /// Whether to auto-approve all tool permission requests. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Optional source for allow-all telemetry. Defaults to `rpc` when omitted for SDK callers. + [JsonPropertyName("source")] + public PermissionsSetApproveAllSource? Source { get; set; } } -/// User response for a pending exit-plan-mode request, with approval state, selected action, auto-approve flag, and feedback. +/// Indicates whether the requested permission mode was applied and reports the authoritative post-mutation mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIExitPlanModeResponse +public sealed class PermissionsSetModeResult { - /// Whether the plan was approved. - [JsonPropertyName("approved")] - public bool Approved { get; set; } - - /// Whether subsequent edits should be auto-approved without confirmation. - [JsonPropertyName("autoApproveEdits")] - public bool? AutoApproveEdits { get; set; } - - /// When true, the agent is instructed to end its turn without starting implementation so the client can restore the session model and auto-submit a fresh implementation turn on it. Set only when a distinct plan configuration (a different model, reasoning effort, or context tier) actually ran the planning turn. - [JsonPropertyName("deferImplementation")] - public bool? DeferImplementation { get; set; } - - /// Feedback from the user when they declined the plan or requested changes. - [JsonPropertyName("feedback")] - public string? Feedback { get; set; } + /// Authoritative permission mode after the mutation. + [JsonPropertyName("mode")] + public PermissionMode Mode { get; set; } - /// The action the user selected. Defaults to 'autopilot' when autoApproveEdits is true, otherwise 'interactive'. - [JsonPropertyName("selectedAction")] - public UIExitPlanModeAction? SelectedAction { get; set; } + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Request ID of a pending `exit_plan_mode.requested` event and the user's response. +/// Permission mode to apply for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIHandlePendingExitPlanModeRequest +internal sealed class PermissionsSetModeRequest { - /// The unique request ID from the exit_plan_mode.requested event. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// Optional judge model id for assisted mode. When omitted, the session resolves the provider default: `gpt-5.5` for CAPI sessions and the active session model for BYOK sessions. + [JsonPropertyName("assistedApprovalModel")] + public string? AssistedApprovalModel { get; set; } - /// User response for a pending exit-plan-mode request, with approval state, selected action, auto-approve flag, and feedback. - [JsonPropertyName("response")] - public UIExitPlanModeResponse Response { get => field ??= new(); set; } + /// Permission mode to apply. + [JsonPropertyName("mode")] + public PermissionMode Mode { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Optional source for permission-mode telemetry. `organization_targeting` is reserved for startup selection after the authenticated account matches an organization targeting policy; SDK callers default to `rpc` and cannot claim targeting provenance. + [JsonPropertyName("source")] + public PermissionModeSource? Source { get; set; } } -/// 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). +/// Current permission mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIRegisterDirectAutoModeSwitchHandlerResult +public sealed class PermissionsGetModeResult { - /// Opaque handle representing the registration. Pass this same handle to `unregisterDirectAutoModeSwitchHandler` when the in-process handler is no longer active. Multiple registrations are reference-counted; the server bridge will only dispatch auto-mode-switch requests when no handles are active. - [JsonPropertyName("handle")] - public string Handle { get; set; } = string.Empty; + /// Current permission mode. + [JsonPropertyName("mode")] + public PermissionMode Mode { get; set; } } -/// Identifies the target session. +/// No parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionUiRegisterDirectAutoModeSwitchHandlerRequest +internal sealed class PermissionsGetModeRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the handle was active and the registration count was decremented. +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIUnregisterDirectAutoModeSwitchHandlerResult +public sealed class PermissionsModifyRulesResult { - /// True if the handle was active and decremented the counter; false if the handle was unknown. - [JsonPropertyName("unregistered")] - public bool Unregistered { get; set; } + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Opaque handle previously returned by `registerDirectAutoModeSwitchHandler` to release. +/// Scope and add/remove instructions for modifying session- or location-scoped permission rules. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIUnregisterDirectAutoModeSwitchHandlerRequest +internal sealed class PermissionsModifyRulesParams { - /// Handle previously returned by `registerDirectAutoModeSwitchHandler`. - [JsonPropertyName("handle")] - public string Handle { get; set; } = string.Empty; + /// Rules to add to the scope. Applied before `remove`/`removeAll`. + [JsonPropertyName("add")] + public IList? Add { get; set; } + + /// Specific rules to remove from the scope. Ignored when `removeAll` is true. + [JsonPropertyName("remove")] + public IList? Remove { get; set; } + + /// When true, removes every rule currently in the scope (after any `add` is applied). Useful for clearing the location scope wholesale. + [JsonPropertyName("removeAll")] + public bool? RemoveAll { get; set; } + + /// Whether the change applies to ephemeral session-scoped rules (cleared at session end) or to location-scoped rules persisted via the location-permissions config file. + [JsonPropertyName("scope")] + public PermissionsModifyRulesScope Scope { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] @@ -22473,427 +25612,279 @@ internal sealed class UIUnregisterDirectAutoModeSwitchHandlerRequest /// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsConfigureResult +public sealed class PermissionsSetRequiredResult { /// Whether the operation succeeded. [JsonPropertyName("success")] public bool Success { get; set; } } -/// Source descriptor for a `session.permissions.configure` content-exclusion rule, with source name and type. +/// Toggles whether permission prompts should be bridged into session events for this client. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsConfigureAdditionalContentExclusionPolicyRuleSource +internal sealed class PermissionsSetRequiredRequest { - /// Name of the policy source. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Whether the client wants `permission.requested` events bridged from the session-owned permission service. CLI clients that render prompt UI set this to `true` for as long as their listener is mounted; headless callers leave it unset (the default is `false`). + [JsonPropertyName("required")] + public bool Required { get; set; } - /// Type of the policy source. - [JsonPropertyName("type")] - public string Type { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Single content-exclusion rule supplied to `session.permissions.configure`, with paths, match conditions, and source. +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsConfigureAdditionalContentExclusionPolicyRule +public sealed class PermissionsResetSessionApprovalsResult { - /// Conditions of which at least one must match. - [JsonPropertyName("ifAnyMatch")] - public IList? IfAnyMatch { get; set; } - - /// Conditions none of which may match. - [JsonPropertyName("ifNoneMatch")] - public IList? IfNoneMatch { get; set; } - - /// Path patterns covered by this rule. - [JsonPropertyName("paths")] - public IList Paths { get => field ??= []; set; } - - /// Source descriptor for a `session.permissions.configure` content-exclusion rule, with source name and type. - [JsonPropertyName("source")] - public PermissionsConfigureAdditionalContentExclusionPolicyRuleSource Source { get => field ??= new(); set; } + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Content-exclusion policy supplied to `session.permissions.configure`, with rules, last-updated data, and scope. +/// Clears session-scoped tool approvals and optionally clears location-scoped approvals and exact session-approved paths. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsConfigureAdditionalContentExclusionPolicy +internal sealed class PermissionsResetSessionApprovalsRequest { - /// Opaque policy update timestamp supplied by the host. - [JsonPropertyName("last_updated_at")] - public JsonElement LastUpdatedAt { get; set; } - - /// Content-exclusion rules to apply. - [JsonPropertyName("rules")] - public IList Rules { get => field ??= []; set; } + /// Whether location-scoped approvals and exact session-approved paths are cleared too. Defaults to `true`. + [JsonPropertyName("includeLocation")] + public bool? IncludeLocation { get; set; } - /// Allowed values for the `PermissionsConfigureAdditionalContentExclusionPolicyScope` enumeration. - [JsonPropertyName("scope")] - public PermissionsConfigureAdditionalContentExclusionPolicyScope Scope { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// If specified, replaces the session's path-permission policy. The runtime constructs the appropriate PathManager based on these inputs (rooted at the session's working directory). Omit to leave the current path policy unchanged. +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionPathsConfig +public sealed class PermissionsNotifyPromptShownResult { - /// Additional directories to allow tool access to (in addition to the session's working directory). Conventional `.github/skills/` and `.github/agents/` definitions under them also join the session catalogs when their subsystem gates are enabled, so supplying a directory is a trust decision for configuration stored there. When `unrestricted` is true, these are still pre-populated on the UnrestrictedPathManager so they remain visible via getDirectories() (e.g. for @-mention completion). - [JsonPropertyName("additionalDirectories")] - public IList? AdditionalDirectories { get; set; } - - /// Whether to include the system temp directory in the allowed list (defaults to true). Ignored when `unrestricted` is true. - [JsonPropertyName("includeTempDirectory")] - public bool? IncludeTempDirectory { get; set; } - - /// If true, the runtime allows access to all paths without prompting. Equivalent to constructing an UnrestrictedPathManager. - [JsonPropertyName("unrestricted")] - public bool? Unrestricted { get; set; } - - /// Workspace root path (special-cased to be allowed even before the directory exists). Ignored when `unrestricted` is true. - [JsonPropertyName("workspacePath")] - public string? WorkspacePath { get; set; } + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// If specified, replaces the session's approved/denied permission rules. Omit to leave the current rules unchanged. +/// Notification payload describing the permission prompt that the client just rendered. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionRulesSet +internal sealed class PermissionPromptShownNotification { - /// Rules that auto-approve matching requests. - [JsonPropertyName("approved")] - public IList Approved { get => field ??= []; set; } + /// Human-readable description of the prompt the user is being asked to approve. Used by the runtime to fire the registered `permission_prompt` notification hook (e.g. terminal bell, desktop notification). + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; - /// Rules that auto-deny matching requests. - [JsonPropertyName("denied")] - public IList Denied { get => field ??= []; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// If specified, replaces the session's URL-permission policy. The runtime constructs a fresh DefaultUrlManager based on these inputs. Omit to leave the current URL policy unchanged. +/// Snapshot of the session's recursive directory grants, exact session-approved paths, and primary working directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionUrlsConfig +public sealed class PermissionPathsList { - /// Initial list of allowed URL/domain patterns. Patterns may include path components. Ignored when `unrestricted` is true. - [JsonPropertyName("initialAllowed")] - public IList? InitialAllowed { get; set; } + /// All directories currently allowed for tool access on this session. + [JsonPropertyName("directories")] + public IList Directories { get => field ??= []; set; } - /// If true, the runtime allows access to all URLs without prompting. Initial allow-list is ignored when this is true. - [JsonPropertyName("unrestricted")] - public bool? Unrestricted { get; set; } + /// The primary working directory for this session. + [JsonPropertyName("primary")] + public string Primary { get; set; } = string.Empty; + + /// Exact paths approved for this session without recursively allowing their descendants. + [JsonPropertyName("sessionApprovedPaths")] + public IList? SessionApprovedPaths { get; set; } } -/// Patch of permission policy fields to apply (omit a field to leave it unchanged). +/// No parameters; returns the session's recursive directory grants and exact session-approved paths. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsConfigureParams +internal sealed class PermissionsPathsListRequest { - /// If specified, replaces the host-supplied GitHub Content Exclusion policies on the session (combined with natively-discovered policies when evaluating tool/file access). Omit to leave the current policies unchanged. - [JsonPropertyName("additionalContentExclusionPolicies")] - public IList? AdditionalContentExclusionPolicies { get; set; } - - /// If specified, sets whether path/URL read permission requests are auto-approved. Omit to leave the current value unchanged. - [JsonPropertyName("approveAllReadPermissionRequests")] - public bool? ApproveAllReadPermissionRequests { get; set; } - - /// If specified, sets whether tool permission requests are auto-approved without prompting. Omit to leave the current value unchanged. - [JsonPropertyName("approveAllToolPermissionRequests")] - public bool? ApproveAllToolPermissionRequests { get; set; } - - /// If specified, replaces the session's path-permission policy. The runtime constructs the appropriate PathManager based on these inputs (rooted at the session's working directory). Omit to leave the current path policy unchanged. - [JsonPropertyName("paths")] - public PermissionPathsConfig? Paths { get; set; } - - /// If specified, replaces the session's approved/denied permission rules. Omit to leave the current rules unchanged. - [JsonPropertyName("rules")] - public PermissionRulesSet? Rules { get; set; } - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// If specified, replaces the session's URL-permission policy. The runtime constructs a fresh DefaultUrlManager based on these inputs. Omit to leave the current URL policy unchanged. - [JsonPropertyName("urls")] - public PermissionUrlsConfig? Urls { get; set; } } -/// Indicates whether the permission decision was applied; false when the request was already resolved. +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionRequestResult +public sealed class PermissionsPathsAddResult { - /// Whether the permission request was handled successfully. + /// Whether the operation succeeded. [JsonPropertyName("success")] public bool Success { get; set; } } -/// The client's response to the pending permission prompt. -/// Polymorphic base type discriminated by kind. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(PermissionDecisionApproveOnce), "approve-once")] -[JsonDerivedType(typeof(PermissionDecisionApproveForSession), "approve-for-session")] -[JsonDerivedType(typeof(PermissionDecisionApproveReadOnlyForSession), "approve-read-only-for-session")] -[JsonDerivedType(typeof(PermissionDecisionApproveForLocation), "approve-for-location")] -[JsonDerivedType(typeof(PermissionDecisionApprovePermanently), "approve-permanently")] -[JsonDerivedType(typeof(PermissionDecisionReject), "reject")] -[JsonDerivedType(typeof(PermissionDecisionUserNotAvailable), "user-not-available")] -[JsonDerivedType(typeof(PermissionDecisionApproved), "approved")] -[JsonDerivedType(typeof(PermissionDecisionApprovedForSession), "approved-for-session")] -[JsonDerivedType(typeof(PermissionDecisionApprovedForLocation), "approved-for-location")] -[JsonDerivedType(typeof(PermissionDecisionCancelled), "cancelled")] -[JsonDerivedType(typeof(PermissionDecisionDeniedByRules), "denied-by-rules")] -[JsonDerivedType(typeof(PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser), "denied-no-approval-rule-and-could-not-request-from-user")] -[JsonDerivedType(typeof(PermissionDecisionDeniedInteractivelyByUser), "denied-interactively-by-user")] -[JsonDerivedType(typeof(PermissionDecisionDeniedByContentExclusionPolicy), "denied-by-content-exclusion-policy")] -[JsonDerivedType(typeof(PermissionDecisionDeniedByPermissionRequestHook), "denied-by-permission-request-hook")] -public partial class PermissionDecision -{ - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} - - -/// Permission-decision request variant to approve only the current permission request. -/// The approve-once variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveOnce : PermissionDecision -{ - /// - [JsonIgnore] - public override string Kind => "approve-once"; - - /// True only when a host surfaced this request to a user who approved it. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("approvedInteractively")] - public bool? ApprovedInteractively { get; set; } -} - -/// Session-scoped approval to remember (tool prompts only; omitted for path/url prompts). -/// Polymorphic base type discriminated by kind. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalCommands), "commands")] -[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalRead), "read")] -[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalWrite), "write")] -[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalMcp), "mcp")] -[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalMcpSampling), "mcp-sampling")] -[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalMemory), "memory")] -[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalCustomTool), "custom-tool")] -[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalExtensionManagement), "extension-management")] -[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalWorkflow), "workflow")] -[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess), "extension-permission-access")] -[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalExtensionEnvAccess), "extension-env-access")] -public partial class PermissionDecisionApproveForSessionApproval -{ - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} - - -/// Session-scoped approval details for specific command identifiers. -/// The commands variant of . +/// Directory path to add to the session's allowed directories. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalCommands : PermissionDecisionApproveForSessionApproval +internal sealed class PermissionPathsAddParams { - /// - [JsonIgnore] - public override string Kind => "commands"; - - /// Command identifiers covered by this approval. - [JsonPropertyName("commandIdentifiers")] - public required IList CommandIdentifiers { get; set; } + /// Directory to add to the allow-list. The runtime resolves and validates the path before adding, then loads conventional `.github/skills/` and `.github/agents/` definitions under it when their subsystem gates are enabled. Adding the directory is therefore also a trust decision for configuration stored there. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Session-scoped approval details for read-only filesystem operations. -/// The read variant of . +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalRead : PermissionDecisionApproveForSessionApproval +public sealed class PermissionsPathsUpdatePrimaryResult { - /// - [JsonIgnore] - public override string Kind => "read"; + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Session-scoped approval details for filesystem write operations. -/// The write variant of . +/// Directory path to set as the session's new primary working directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalWrite : PermissionDecisionApproveForSessionApproval +internal sealed class PermissionPathsUpdatePrimaryParams { - /// - [JsonIgnore] - public override string Kind => "write"; + /// Directory to set as the new primary working directory for the session's permission policy. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Session-scoped approval details for an MCP server tool, or all tools on the server when `toolName` is null. -/// The mcp variant of . +/// Indicates whether the supplied path is within the session's allowed directories. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalMcp : PermissionDecisionApproveForSessionApproval +public sealed class PermissionPathsAllowedCheckResult { - /// - [JsonIgnore] - public override string Kind => "mcp"; - - /// MCP server name. - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } - - /// MCP tool name, or null to cover every tool on the server. - [JsonPropertyName("toolName")] - public string? ToolName { get; set; } + /// Whether the path is within the session's allowed directories. + [JsonPropertyName("allowed")] + public bool Allowed { get; set; } } -/// Session-scoped approval details for MCP sampling requests from a server. -/// The mcp-sampling variant of . +/// Path to evaluate against the session's allowed directories. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalMcpSampling : PermissionDecisionApproveForSessionApproval +internal sealed class PermissionPathsAllowedCheckParams { - /// - [JsonIgnore] - public override string Kind => "mcp-sampling"; + /// Path to check against the session's allowed directories. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; - /// MCP server name. - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Session-scoped approval details for writes to long-term memory. -/// The memory variant of . +/// Indicates whether the supplied path is within the session's workspace directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalMemory : PermissionDecisionApproveForSessionApproval +public sealed class PermissionPathsWorkspaceCheckResult { - /// - [JsonIgnore] - public override string Kind => "memory"; + /// Whether the path is within the session workspace directory. + [JsonPropertyName("allowed")] + public bool Allowed { get; set; } } -/// Session-scoped approval details for a custom tool, keyed by tool name. -/// The custom-tool variant of . +/// Path to evaluate against the session's workspace (primary) directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalCustomTool : PermissionDecisionApproveForSessionApproval +internal sealed class PermissionPathsWorkspaceCheckParams { - /// - [JsonIgnore] - public override string Kind => "custom-tool"; + /// Path to check against the session workspace directory. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; - /// Custom tool name. - [JsonPropertyName("toolName")] - public required string ToolName { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Session-scoped approval details for extension-management operations, optionally narrowed by operation. -/// The extension-management variant of . +/// Resolved location-permissions key and type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalExtensionManagement : PermissionDecisionApproveForSessionApproval +public sealed class PermissionLocationResolveResult { - /// - [JsonIgnore] - public override string Kind => "extension-management"; + /// Location key used in the location-permissions store. + [JsonPropertyName("locationKey")] + public string LocationKey { get; set; } = string.Empty; - /// Optional operation identifier; when omitted, the approval covers all extension management operations. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("operation")] - public string? Operation { get; set; } + /// Whether the location is a git repo or directory. + [JsonPropertyName("locationType")] + public PermissionLocationType LocationType { get; set; } } -/// Session-scoped workflow approval, optionally narrowed by approval key. -/// The workflow variant of . +/// Working directory to resolve into a location-permissions key. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalWorkflow : PermissionDecisionApproveForSessionApproval +internal sealed class PermissionLocationResolveParams { - /// - [JsonIgnore] - public override string Kind => "workflow"; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Optional workflow operation name or canonical approval key; when omitted, the approval covers all workflow operations. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("approvalKey")] - public string? ApprovalKey { get; set; } + /// Working directory whose permission location should be resolved. + [JsonPropertyName("workingDirectory")] + public string WorkingDirectory { get; set; } = string.Empty; } -/// Session-scoped approval details for an extension's permission-gated capability access, keyed by extension name. -/// The extension-permission-access variant of . +/// Summary of persisted location permissions applied to the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess : PermissionDecisionApproveForSessionApproval +public sealed class PermissionLocationApplyResult { - /// - [JsonIgnore] - public override string Kind => "extension-permission-access"; + /// Number of persisted allowed directories added to the live path manager. + [JsonPropertyName("appliedDirectoryCount")] + public long AppliedDirectoryCount { get; set; } - /// Extension name. - [JsonPropertyName("extensionName")] - public required string ExtensionName { get; set; } -} + /// Number of location-scoped rules added to the live permission service. + [JsonPropertyName("appliedRuleCount")] + public long AppliedRuleCount { get; set; } -/// Session-scoped approval details for an extension's access to sensitive environment variables, keyed by extension name and the exact set of variable names. -/// The extension-env-access variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalExtensionEnvAccess : PermissionDecisionApproveForSessionApproval -{ - /// - [JsonIgnore] - public override string Kind => "extension-env-access"; + /// Location-scoped rules applied to the live permission service. + [JsonPropertyName("appliedRules")] + public IList AppliedRules { get => field ??= []; set; } - /// Names of the sensitive environment variables this approval covers. Values are never persisted. - [JsonPropertyName("environmentVariables")] - public required IList EnvironmentVariables { get; set; } + /// Whether a different location was applied since the previous apply call. + [JsonPropertyName("changed")] + public bool Changed { get; set; } - /// Extension name. - [JsonPropertyName("extensionName")] - public required string ExtensionName { get; set; } + /// Location key used in the location-permissions store. + [JsonPropertyName("locationKey")] + public string LocationKey { get; set; } = string.Empty; + + /// Whether the location is a git repo or directory. + [JsonPropertyName("locationType")] + public PermissionLocationType LocationType { get; set; } } -/// Permission-decision request variant to approve for the rest of the session, with optional tool approval or URL domain. -/// The approve-for-session variant of . +/// Working directory to load persisted location permissions for. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSession : PermissionDecision +internal sealed class PermissionLocationApplyParams { - /// - [JsonIgnore] - public override string Kind => "approve-for-session"; - - /// Session-scoped approval to remember (tool prompts only; omitted for path/url prompts). - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("approval")] - public PermissionDecisionApproveForSessionApproval? Approval { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// URL domain to approve for the rest of the session (URL prompts only). - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("domain")] - public string? Domain { get; set; } + /// Working directory whose persisted location permissions should be applied. + [JsonPropertyName("workingDirectory")] + public string WorkingDirectory { get; set; } = string.Empty; } -/// Approve file-tool read access to specific directories for this logical session, including continuation or resume. -/// The approve-read-only-for-session variant of . +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveReadOnlyForSession : PermissionDecision +public sealed class PermissionsLocationsAddToolApprovalResult { - /// - [JsonIgnore] - public override string Kind => "approve-read-only-for-session"; - - /// Canonical directories covered by the read-only grant. - [JsonPropertyName("directories")] - public required IList Directories { get; set; } + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Approval to persist for this location. +/// Tool approval to persist and apply. /// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonPolymorphic( TypeDiscriminatorPropertyName = "kind", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalCommands), "commands")] -[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalRead), "read")] -[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalWrite), "write")] -[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalMcp), "mcp")] -[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalMcpSampling), "mcp-sampling")] -[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalMemory), "memory")] -[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalCustomTool), "custom-tool")] -[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalExtensionManagement), "extension-management")] -[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalWorkflow), "workflow")] -[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess), "extension-permission-access")] -[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalExtensionEnvAccess), "extension-env-access")] -public partial class PermissionDecisionApproveForLocationApproval +[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsCommands), "commands")] +[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsRead), "read")] +[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsWrite), "write")] +[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsMcp), "mcp")] +[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsMcpSampling), "mcp-sampling")] +[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsMemory), "memory")] +[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsCustomTool), "custom-tool")] +[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsExtensionManagement), "extension-management")] +[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsWorkflow), "workflow")] +[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess), "extension-permission-access")] +[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsExtensionEnvAccess), "extension-env-access")] +public partial class PermissionsLocationsAddToolApprovalDetails { /// The type discriminator. [JsonPropertyName("kind")] @@ -22901,10 +25892,10 @@ public partial class PermissionDecisionApproveForLocationApproval } -/// Location-scoped approval details for specific command identifiers. -/// The commands variant of . +/// Location-persisted tool approval details for specific command identifiers. +/// The commands variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalCommands : PermissionDecisionApproveForLocationApproval +public partial class PermissionsLocationsAddToolApprovalDetailsCommands : PermissionsLocationsAddToolApprovalDetails { /// [JsonIgnore] @@ -22915,30 +25906,30 @@ public partial class PermissionDecisionApproveForLocationApprovalCommands : Perm public required IList CommandIdentifiers { get; set; } } -/// Location-scoped approval details for read-only filesystem operations. -/// The read variant of . +/// Location-persisted tool approval details for read-only filesystem operations. +/// The read variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalRead : PermissionDecisionApproveForLocationApproval +public partial class PermissionsLocationsAddToolApprovalDetailsRead : PermissionsLocationsAddToolApprovalDetails { /// [JsonIgnore] public override string Kind => "read"; } -/// Location-scoped approval details for filesystem write operations. -/// The write variant of . +/// Location-persisted tool approval details for filesystem write operations. +/// The write variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalWrite : PermissionDecisionApproveForLocationApproval +public partial class PermissionsLocationsAddToolApprovalDetailsWrite : PermissionsLocationsAddToolApprovalDetails { /// [JsonIgnore] public override string Kind => "write"; } -/// Location-scoped approval details for an MCP server tool, or all tools on the server when `toolName` is null. -/// The mcp variant of . +/// Location-persisted tool approval details for an MCP server tool, or all tools when `toolName` is null. +/// The mcp variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalMcp : PermissionDecisionApproveForLocationApproval +public partial class PermissionsLocationsAddToolApprovalDetailsMcp : PermissionsLocationsAddToolApprovalDetails { /// [JsonIgnore] @@ -22953,10 +25944,10 @@ public partial class PermissionDecisionApproveForLocationApprovalMcp : Permissio public string? ToolName { get; set; } } -/// Location-scoped approval details for MCP sampling requests from a server. -/// The mcp-sampling variant of . +/// Location-persisted tool approval details for MCP sampling requests from a server. +/// The mcp-sampling variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalMcpSampling : PermissionDecisionApproveForLocationApproval +public partial class PermissionsLocationsAddToolApprovalDetailsMcpSampling : PermissionsLocationsAddToolApprovalDetails { /// [JsonIgnore] @@ -22967,20 +25958,20 @@ public partial class PermissionDecisionApproveForLocationApprovalMcpSampling : P public required string ServerName { get; set; } } -/// Location-scoped approval details for writes to long-term memory. -/// The memory variant of . +/// Location-persisted tool approval details for writes to long-term memory. +/// The memory variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalMemory : PermissionDecisionApproveForLocationApproval +public partial class PermissionsLocationsAddToolApprovalDetailsMemory : PermissionsLocationsAddToolApprovalDetails { /// [JsonIgnore] public override string Kind => "memory"; } -/// Location-scoped approval details for a custom tool, keyed by tool name. -/// The custom-tool variant of . +/// Location-persisted tool approval details for a custom tool, keyed by tool name. +/// The custom-tool variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalCustomTool : PermissionDecisionApproveForLocationApproval +public partial class PermissionsLocationsAddToolApprovalDetailsCustomTool : PermissionsLocationsAddToolApprovalDetails { /// [JsonIgnore] @@ -22991,10 +25982,10 @@ public partial class PermissionDecisionApproveForLocationApprovalCustomTool : Pe public required string ToolName { get; set; } } -/// Location-scoped approval details for extension-management operations, optionally narrowed by operation. -/// The extension-management variant of . +/// Location-persisted tool approval details for extension-management operations, optionally narrowed by operation. +/// The extension-management variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalExtensionManagement : PermissionDecisionApproveForLocationApproval +public partial class PermissionsLocationsAddToolApprovalDetailsExtensionManagement : PermissionsLocationsAddToolApprovalDetails { /// [JsonIgnore] @@ -23006,10 +25997,10 @@ public partial class PermissionDecisionApproveForLocationApprovalExtensionManage public string? Operation { get; set; } } -/// Location-scoped workflow approval, optionally narrowed by approval key. -/// The workflow variant of . +/// Location-persisted workflow approval, optionally narrowed by approval key. +/// The workflow variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalWorkflow : PermissionDecisionApproveForLocationApproval +public partial class PermissionsLocationsAddToolApprovalDetailsWorkflow : PermissionsLocationsAddToolApprovalDetails { /// [JsonIgnore] @@ -23021,10 +26012,10 @@ public partial class PermissionDecisionApproveForLocationApprovalWorkflow : Perm public string? ApprovalKey { get; set; } } -/// Location-scoped approval details for an extension's permission-gated capability access, keyed by extension name. -/// The extension-permission-access variant of . +/// Location-persisted tool approval details for an extension's permission-gated capability access, keyed by extension name. +/// The extension-permission-access variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess : PermissionDecisionApproveForLocationApproval +public partial class PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess : PermissionsLocationsAddToolApprovalDetails { /// [JsonIgnore] @@ -23035,10 +26026,10 @@ public partial class PermissionDecisionApproveForLocationApprovalExtensionPermis public required string ExtensionName { get; set; } } -/// Location-scoped approval details for an extension's access to sensitive environment variables, keyed by extension name and the exact set of variable names. -/// The extension-env-access variant of . +/// Location-persisted tool approval details for an extension's access to sensitive environment variables, keyed by extension name and the exact set of variable names. +/// The extension-env-access variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalExtensionEnvAccess : PermissionDecisionApproveForLocationApproval +public partial class PermissionsLocationsAddToolApprovalDetailsExtensionEnvAccess : PermissionsLocationsAddToolApprovalDetails { /// [JsonIgnore] @@ -23053,4909 +26044,5332 @@ public partial class PermissionDecisionApproveForLocationApprovalExtensionEnvAcc public required string ExtensionName { get; set; } } -/// Permission-decision request variant to approve and persist a permission for a project location, with approval details and location key. -/// The approve-for-location variant of . +/// Location-scoped tool approval to persist. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocation : PermissionDecision +internal sealed class PermissionLocationAddToolApprovalParams { - /// - [JsonIgnore] - public override string Kind => "approve-for-location"; - - /// Approval to persist for this location. + /// Tool approval to persist and apply. [JsonPropertyName("approval")] - public required PermissionDecisionApproveForLocationApproval Approval { get; set; } + public PermissionsLocationsAddToolApprovalDetails Approval { get => field ??= new(); set; } /// Location key (git root or cwd) to persist the approval to. [JsonPropertyName("locationKey")] - public required string LocationKey { get; set; } + public string LocationKey { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Permission-decision request variant to permanently approve a URL domain across sessions. -/// The approve-permanently variant of . +/// Folder path to check for trust. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApprovePermanently : PermissionDecision +internal sealed class FolderTrustCheckParams { - /// - [JsonIgnore] - public override string Kind => "approve-permanently"; + /// Folder path to check. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; - /// URL domain to approve permanently. - [JsonPropertyName("domain")] - public required string Domain { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Permission-decision request variant to reject a pending permission request, with optional feedback. -/// The reject variant of . +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionReject : PermissionDecision +public sealed class PermissionsFolderTrustAddTrustedResult { - /// - [JsonIgnore] - public override string Kind => "reject"; - - /// Optional feedback explaining the rejection. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("feedback")] - public string? Feedback { get; set; } + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Permission-decision variant indicating no user was available to confirm the request. -/// The user-not-available variant of . +/// Folder path to add to trusted folders. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionUserNotAvailable : PermissionDecision +internal sealed class FolderTrustAddParams { - /// - [JsonIgnore] - public override string Kind => "user-not-available"; + /// Folder path to mark as trusted. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Permission-decision variant indicating the request was approved. -/// The approved variant of . +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproved : PermissionDecision +public sealed class PermissionsUrlsSetUnrestrictedModeResult { - /// - [JsonIgnore] - public override string Kind => "approved"; + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Permission-decision variant indicating approval was remembered for the session, with approval details. -/// The approved-for-session variant of . +/// Whether the URL-permission policy should run in unrestricted mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApprovedForSession : PermissionDecision +internal sealed class PermissionUrlsSetUnrestrictedModeParams { - /// - [JsonIgnore] - public override string Kind => "approved-for-session"; + /// Whether to allow access to all URLs without prompting. Toggles the runtime's URL-permission policy in place. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } - /// The approval to add as a session-scoped rule. - [JsonPropertyName("approval")] - public required UserToolSessionApproval Approval { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Permission-decision variant indicating approval was persisted for a project location, with approval details and location key. -/// The approved-for-location variant of . +/// The repository the remote session targets. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApprovedForLocation : PermissionDecision +public sealed class MetadataSnapshotRemoteMetadataRepository { - /// - [JsonIgnore] - public override string Kind => "approved-for-location"; + /// The branch the remote session is operating on. + [JsonPropertyName("branch")] + public string Branch { get; set; } = string.Empty; - /// The approval to persist for this location. - [JsonPropertyName("approval")] - public required UserToolSessionApproval Approval { get; set; } + /// The GitHub repository name (without owner). + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// The location key (git root or cwd) to persist the approval to. - [JsonPropertyName("locationKey")] - public required string LocationKey { get; set; } + /// The GitHub owner (user or organization) of the target repository. + [JsonPropertyName("owner")] + public string Owner { get; set; } = string.Empty; } -/// Permission-decision variant indicating the request was cancelled before use, with an optional reason. -/// The cancelled variant of . +/// Remote-session-specific metadata. Populated only when `isRemote` is true. Fields are immutable for the lifetime of the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionCancelled : PermissionDecision +public sealed class MetadataSnapshotRemoteMetadata { - /// - [JsonIgnore] - public override string Kind => "cancelled"; + /// The pull request number the remote session is associated with, if any. + [JsonPropertyName("pullRequestNumber")] + public long? PullRequestNumber { get; set; } - /// Optional explanation of why the request was cancelled. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("reason")] - public string? Reason { get; set; } -} + /// The repository the remote session targets. + [JsonPropertyName("repository")] + public MetadataSnapshotRemoteMetadataRepository Repository { get => field ??= new(); set; } -/// Permission-decision variant indicating explicit denial by permission rules, with the matching rules. -/// The denied-by-rules variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionDeniedByRules : PermissionDecision -{ - /// - [JsonIgnore] - public override string Kind => "denied-by-rules"; + /// The original resource identifier (task ID or PR node ID), preserved across event-replay reconstructions. Falls back to `sessionId` when absent. + [JsonPropertyName("resourceId")] + public string? ResourceId { get; set; } - /// Rules that denied the request. - [JsonPropertyName("rules")] - public required IList Rules { get; set; } + /// Whether the remote task originated from Copilot Coding Agent (cca) or a CLI `--remote` invocation. + [JsonPropertyName("taskType")] + public MetadataSnapshotRemoteMetadataTaskType? TaskType { get; set; } } -/// Permission-decision variant indicating no approval rule matched and user confirmation was unavailable. -/// The denied-no-approval-rule-and-could-not-request-from-user variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser : PermissionDecision +/// Public-facing projection of workspace metadata for SDK / TUI consumers. +public sealed class SessionMetadataSnapshotWorkspace { - /// - [JsonIgnore] - public override string Kind => "denied-no-approval-rule-and-could-not-request-from-user"; -} + /// Branch checked out at session start, if any. + [JsonPropertyName("branch")] + public string? Branch { get; set; } -/// Permission-decision variant indicating the user denied an interactive prompt, with optional feedback and force-reject flag. -/// The denied-interactively-by-user variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionDeniedInteractivelyByUser : PermissionDecision -{ - /// - [JsonIgnore] - public override string Kind => "denied-interactively-by-user"; + /// ISO 8601 timestamp when the workspace was created. + [JsonPropertyName("created_at")] + public DateTimeOffset? CreatedAt { get; set; } - /// Optional feedback from the user explaining the denial. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("feedback")] - public string? Feedback { get; set; } + /// Current working directory at session start. + [JsonPropertyName("cwd")] + public string? Cwd { get; set; } + + /// Resolved git root for cwd, if any. + [JsonPropertyName("git_root")] + public string? GitRoot { get; set; } + + /// Repository host type, if known. + [JsonPropertyName("host_type")] + public WorkspaceSummaryHostType? HostType { get; set; } + + /// Workspace identifier (1:1 with sessionId). + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Display name for the session, if set. + [JsonPropertyName("name")] + public string? Name { get; set; } + + /// Repository identifier in 'owner/repo' or 'org/project/repo' format, if any. + [JsonPropertyName("repository")] + public string? Repository { get; set; } - /// Whether to force-reject the current agent turn. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("forceReject")] - public bool? ForceReject { get; set; } + /// ISO 8601 timestamp when the workspace was last updated. + [JsonPropertyName("updated_at")] + public DateTimeOffset? UpdatedAt { get; set; } + + /// Whether the display name was explicitly set by the user. + [JsonPropertyName("user_named")] + public bool? UserNamed { get; set; } } -/// Permission-decision variant indicating denial by content-exclusion policy, with path and message. -/// The denied-by-content-exclusion-policy variant of . +/// Point-in-time snapshot of slow-changing session identifier and state fields. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionDeniedByContentExclusionPolicy : PermissionDecision +public sealed class SessionMetadataSnapshot { - /// - [JsonIgnore] - public override string Kind => "denied-by-content-exclusion-policy"; + /// True when the session was detected to be in use by another process at construction time. Local consumers may surface a confirmation prompt before fully attaching. Always false for new sessions. + [JsonPropertyName("alreadyInUse")] + public bool AlreadyInUse { get; set; } - /// Human-readable explanation of why the path was excluded. - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Runtime client name associated with the session (telemetry identifier). + [JsonPropertyName("clientName")] + public string? ClientName { get; set; } - /// File path that triggered the exclusion. - [JsonPropertyName("path")] - public required string Path { get; set; } -} + /// The current agent mode for this session (e.g., 'interactive', 'plan', 'autopilot'). + [JsonPropertyName("currentMode")] + public MetadataSnapshotCurrentMode CurrentMode { get; set; } -/// Permission-decision variant indicating denial by a permission request hook, with optional message and interrupt flag. -/// The denied-by-permission-request-hook variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionDeniedByPermissionRequestHook : PermissionDecision -{ - /// - [JsonIgnore] - public override string Kind => "denied-by-permission-request-hook"; + /// Live indexed-search state for this session activation. Omitted by runtimes that do not expose indexed-search status; absence does not indicate enablement. + [JsonPropertyName("indexedSearch")] + public IndexedSearchState? IndexedSearch { get; set; } - /// Whether to interrupt the current agent turn. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("interrupt")] - public bool? Interrupt { get; set; } + /// User-provided name supplied at session construction (via `--name`), if any. Immutable after construction. + [JsonPropertyName("initialName")] + public string? InitialName { get; set; } - /// Optional message from the hook explaining the denial. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("message")] - public string? Message { get; set; } -} + /// Whether this is a remote session (i.e., one whose runtime executes elsewhere and is steered through this process). + [JsonPropertyName("isRemote")] + public bool IsRemote { get; set; } -/// Pending permission request ID and the decision to apply (approve/reject and scope). -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionDecisionRequest -{ - /// Optional informational context describing how and where this response was made. Omit it to preserve legacy behavior without attributing an origin. - [JsonPropertyName("decisionContext")] - public PermissionDecisionContext? DecisionContext { get; set; } + /// ISO 8601 timestamp of when the session's persisted state was last modified on disk. For new sessions, equals startTime. For resumed sessions, reflects the previous modification time at construction. + [JsonPropertyName("modifiedTime")] + public DateTimeOffset ModifiedTime { get; set; } - /// Request ID of the pending permission request. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// Remote-session-specific metadata. Populated only when `isRemote` is true. Fields are immutable for the lifetime of the session. + [JsonPropertyName("remoteMetadata")] + public MetadataSnapshotRemoteMetadata? RemoteMetadata { get; set; } - /// The client's response to the pending permission prompt. - [JsonPropertyName("result")] - public PermissionDecision Result { get => field ??= new(); set; } + /// Currently selected model identifier, if any. + [JsonPropertyName("selectedModel")] + public string? SelectedModel { get; set; } - /// Target session identifier. + /// The unique identifier of the session. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; -} -/// Pending permission prompt reconstructed from event history, with request ID and user-facing prompt details. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PendingPermissionRequest -{ - /// Permission-recovery episode that authorized this request to surface for interactive attention. - [JsonPropertyName("recoveryEpisodeId")] - public string? RecoveryEpisodeId { get; set; } + /// Current session limits, or null when no limits are active. + [JsonPropertyName("sessionLimits")] + public SessionLimitsConfig? SessionLimits { get; set; } - /// The user-facing permission prompt details (commands, write, read, mcp, url, memory, custom-tool, path, hook). - [JsonPropertyName("request")] - public PermissionPromptRequest Request { get; set; } = null!; + /// ISO 8601 timestamp of when the session started. + [JsonPropertyName("startTime")] + public DateTimeOffset StartTime { get; set; } - /// Unique identifier for the pending permission request. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; -} + /// Short human-readable summary of the session, if known. Omitted when no summary has been generated. + [JsonPropertyName("summary")] + public string? Summary { get; set; } -/// List of pending permission requests reconstructed from event history. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PendingPermissionRequestList -{ - /// Pending permission prompts reconstructed from the session's event history. Equivalent to the set of `permission.requested` events that have not yet been followed by a matching `permission.completed` event. Used by clients (e.g. the CLI) to hydrate UI for prompts that were emitted before the client attached to the session. - [JsonPropertyName("items")] - public IList Items { get => field ??= []; set; } + /// Absolute path to the session's current working directory. + [JsonPropertyName("workingDirectory")] + public string WorkingDirectory { get; set; } = string.Empty; + + /// Public-facing workspace metadata for this session, or null if the session has no associated workspace. Excludes runtime-internal fields (GitHub IDs, summary count, internal flags). + [JsonPropertyName("workspace")] + public SessionMetadataSnapshotWorkspace? Workspace { get; set; } + + /// Absolute path to the session's workspace directory on disk, or null if the session has no associated workspace. + [JsonPropertyName("workspacePath")] + public string? WorkspacePath { get; set; } } -/// No parameters; returns currently-pending permission requests for the session. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsPendingRequestsRequest +internal sealed class SessionMetadataSnapshotRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the operation succeeded. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsSetApproveAllResult +internal sealed class SessionMetadataGetClientMetadataRequest { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Allow-all toggle for tool permission requests, with an optional telemetry source. +/// Atomic patch for client-owned session metadata. Operations apply in clear, remove, then set order. The resulting bag must satisfy the ClientMetadata entry and serialized-size limits. Local storage coordinates concurrent runtime processes; custom SessionFs providers must serialize writers that access the same session from multiple processes. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsSetApproveAllRequest +internal sealed class MetadataUpdateClientMetadataRequest { - /// Whether to auto-approve all tool permission requests. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } + /// Remove every existing client metadata entry before applying remove and set. Defaults to false. + [JsonPropertyName("clear")] + public bool? Clear { get; set; } + + /// Case-sensitive keys to remove. Missing keys are ignored. Each key must be non-empty, at most 256 UTF-8 bytes, and outside the reserved `copilot/` and `github/` namespaces. + [JsonPropertyName("remove")] + public IList? Remove { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - /// Optional source for allow-all telemetry. Defaults to `rpc` when omitted for SDK callers. - [JsonPropertyName("source")] - public PermissionsSetApproveAllSource? Source { get; set; } + /// String entries to add or replace. Set wins when a key also appears in remove. Each key must be non-empty, at most 256 UTF-8 bytes, and outside the reserved `copilot/` and `github/` namespaces. Each value may contain at most 16 KiB of UTF-8 data. + [JsonPropertyName("set")] + public IDictionary? Set { get; set; } } -/// Indicates whether the requested permission mode was applied and reports the authoritative post-mutation mode. +/// Indicates whether the local session is currently processing a turn or background continuation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsSetModeResult +public sealed class MetadataIsProcessingResult { - /// Authoritative permission mode after the mutation. - [JsonPropertyName("mode")] - public PermissionMode Mode { get; set; } - - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Whether the session is currently processing user/agent messages. False for non-local sessions (which don't run a local agentic loop). Reflects an in-flight turn or background continuation. + [JsonPropertyName("processing")] + public bool Processing { get; set; } } -/// Permission mode to apply for the session. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsSetModeRequest +internal sealed class SessionMetadataIsProcessingRequest { - /// Optional judge model id for assisted mode. When omitted, the session resolves the provider default: `gpt-5.5` for CAPI sessions and the active session model for BYOK sessions. - [JsonPropertyName("assistedApprovalModel")] - public string? AssistedApprovalModel { get; set; } - - /// Permission mode to apply. - [JsonPropertyName("mode")] - public PermissionMode Mode { get; set; } - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// Optional source for permission-mode telemetry. `organization_targeting` is reserved for startup selection after the authenticated account matches an organization targeting policy; SDK callers default to `rpc` and cannot claim targeting provenance. - [JsonPropertyName("source")] - public PermissionModeSource? Source { get; set; } } -/// Current permission mode. +/// Current activity flags for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsGetModeResult +public sealed class SessionActivity { - /// Current permission mode. - [JsonPropertyName("mode")] - public PermissionMode Mode { get; set; } + /// Whether an in-flight operation can currently be aborted. + [JsonPropertyName("abortable")] + public bool Abortable { get; set; } + + /// Whether the session currently has active work, including running turns or tasks. + [JsonPropertyName("hasActiveWork")] + public bool HasActiveWork { get; set; } } -/// No parameters. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsGetModeRequest +internal sealed class SessionMetadataActivityRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the operation succeeded. +/// Token-usage breakdown for the session's current context window. +public sealed class MetadataContextInfoResultContextInfo +{ + /// Output reservation overlapping the displayed prompt allowance plus tokens after the effective input budget's buffer-exhaustion blocking threshold (default 95%). + [JsonPropertyName("bufferTokens")] + public long BufferTokens { get; set; } + + /// Token count at which background compaction starts (configurable percentage of promptTokenLimit). + [JsonPropertyName("compactionThreshold")] + public long CompactionThreshold { get; set; } + + /// Tokens consumed by user/assistant/tool messages. + [JsonPropertyName("conversationTokens")] + public long ConversationTokens { get; set; } + + /// Provider-owned model display label, or the Auto routing label. Presentation only; modelName remains the tokenization identity. + [JsonPropertyName("displayModelName")] + public string? DisplayModelName { get; set; } + + /// Advertised prompt allowance for the selected context tier, without adding output tokens. The denominator for context-usage displays. + [JsonPropertyName("limit")] + public long Limit { get; set; } + + /// Tokens consumed by MCP tool definitions (subset of toolDefinitionsTokens, excludes deferred tools). + [JsonPropertyName("mcpToolsTokens")] + public long McpToolsTokens { get; set; } + + /// The model used for token counting. + [JsonPropertyName("modelName")] + public string ModelName { get; set; } = string.Empty; + + /// Effective input budget: the selected tier's prompt allowance bounded by the combined context ceiling minus the requested output allowance. Uses DEFAULT_TOKEN_LIMIT when limits are unspecified. + [JsonPropertyName("promptTokenLimit")] + public long PromptTokenLimit { get; set; } + + /// Provider whose catalog supplies this context window. Omitted when provider identity is unavailable. + [JsonPropertyName("provider")] + public ModelProviderRef? Provider { get; set; } + + /// Tokens consumed by the system prompt. + [JsonPropertyName("systemTokens")] + public long SystemTokens { get; set; } + + /// Tokens consumed by tool definitions sent to the model (excludes deferred tools). + [JsonPropertyName("toolDefinitionsTokens")] + public long ToolDefinitionsTokens { get; set; } + + /// Sum of system, conversation and tool-definition tokens. + [JsonPropertyName("totalTokens")] + public long TotalTokens { get; set; } +} + +/// Token breakdown for the session's current context window, or null if uninitialized. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsModifyRulesResult +public sealed class MetadataContextInfoResult { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Token breakdown for the current context window, or null if the session has not yet been initialized (no system prompt or tool metadata cached). + [JsonPropertyName("contextInfo")] + public MetadataContextInfoResultContextInfo? ContextInfo { get; set; } } -/// Scope and add/remove instructions for modifying session- or location-scoped permission rules. +/// Model identifier and token limits used to compute the context-info breakdown. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsModifyRulesParams +internal sealed class MetadataContextInfoRequest { - /// Rules to add to the scope. Applied before `remove`/`removeAll`. - [JsonPropertyName("add")] - public IList? Add { get; set; } + /// Requested output allowance to reserve against the combined context ceiling. Pass 0 to resolve the session's request cap, falling back to the model's advertised output limit. + [JsonPropertyName("outputTokenLimit")] + public long OutputTokenLimit { get; set; } - /// Specific rules to remove from the scope. Ignored when `removeAll` is true. - [JsonPropertyName("remove")] - public IList? Remove { get; set; } + /// Advertised prompt allowance. Pass 0 to resolve the selected model and context tier from the session. + [JsonPropertyName("promptTokenLimit")] + public long PromptTokenLimit { get; set; } - /// When true, removes every rule currently in the scope (after any `add` is applied). Useful for clearing the location scope wholesale. - [JsonPropertyName("removeAll")] - public bool? RemoveAll { get; set; } + /// Provider owning selectedModel. Omit to use the current provider for the current model, or deterministic legacy lookup for a different model. + [JsonPropertyName("providerId")] + public string? ProviderId { get; set; } - /// Whether the change applies to ephemeral session-scoped rules (cleared at session end) or to location-scoped rules persisted via the location-permissions config file. - [JsonPropertyName("scope")] - public PermissionsModifyRulesScope Scope { get; set; } + /// Model identifier used for tokenization. Omit to use the session default. Used both for token counting and to compute display values. + [JsonPropertyName("selectedModel")] + public string? SelectedModel { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the operation succeeded. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsSetRequiredResult +/// The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. +public sealed class MetadataContextAttributionResultContextAttributionCategories { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Overlapping output reservation plus post-blocking-threshold buffer. + [JsonPropertyName("buffer")] + public long Buffer { get; set; } + + /// Custom-instructions tokens (0 when none are configured). + [JsonPropertyName("customInstructions")] + public long CustomInstructions { get; set; } + + /// Remaining unused window capacity (clamped at 0). + [JsonPropertyName("freeSpace")] + public long FreeSpace { get; set; } + + /// MCP tool-definition tokens. + [JsonPropertyName("mcpTools")] + public long McpTools { get; set; } + + /// Conversation (user/assistant/tool) message tokens. + [JsonPropertyName("messages")] + public long Messages { get; set; } + + /// System prompt tokens, excluding custom instructions. + [JsonPropertyName("systemPrompt")] + public long SystemPrompt { get; set; } + + /// Non-MCP tool-definition tokens. + [JsonPropertyName("systemTools")] + public long SystemTools { get; set; } } -/// Toggles whether permission prompts should be bridged into session events for this client. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsSetRequiredRequest +/// Successful compaction history for the session. +public sealed class MetadataContextAttributionResultContextAttributionCompactions { - /// Whether the client wants `permission.requested` events bridged from the session-owned permission service. CLI clients that render prompt UI set this to `true` for as long as their listener is mounted; headless callers leave it unset (the default is `false`). - [JsonPropertyName("required")] - public bool Required { get; set; } - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Number of successful compactions in this session. + [JsonPropertyName("count")] + public long Count { get; set; } } -/// Indicates whether the operation succeeded. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsResetSessionApprovalsResult +/// RPC data type for MetadataContextAttributionResultContextAttributionEntry operations. +public sealed class MetadataContextAttributionResultContextAttributionEntry { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Supplementary per-entry metadata (e.g. `messageCount`, `role`, `evictable`, `pluginSource`). Values are stringified; parse as needed and ignore unrecognized keys. + [JsonPropertyName("attributes")] + public IDictionary? Attributes { get; set; } + + /// Identifier for this entry, formed by joining its `kind` and source name (e.g. `tool:bash`, `skill:tmux`, `toolDefinition:bash`); unique within the snapshot. Use it to match the same entry across snapshots, to correlate with other APIs (skill/agent/MCP registries), and as the `parentId` target for nesting. Distinct from the human-facing `label`. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Source category for this entry. Not a closed set — tolerate unknown values. Known values today: `skill`, `subagent`, `mcpServer`, `tool`, `system`, `toolDefinition`, `plugin`. + [JsonPropertyName("kind")] + public string Kind { get; set; } = string.Empty; + + /// Human-readable display label, e.g. `bash` or `skill: tmux`. Presentation-only; may be localized/reformatted without notice — do not key off it. + [JsonPropertyName("label")] + public string Label { get; set; } = string.Empty; + + /// Optional `id` of the parent entry: e.g. a `plugin` entry parenting its `skill`/`mcpServer` entries, or the `system` entry parenting `toolDefinition` entries. Omitted for top-level entries. + [JsonPropertyName("parentId")] + public string? ParentId { get; set; } + + /// Token count currently in context attributable to this entry. + [JsonPropertyName("tokens")] + public long Tokens { get; set; } } -/// Clears session-scoped tool approvals and optionally clears location-scoped approvals and exact session-approved paths. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsResetSessionApprovalsRequest +/// Per-source token attribution snapshot for the current context window. The heaviest individual messages are available separately via `metadata.getContextHeaviestMessages`. +public sealed class MetadataContextAttributionResultContextAttribution { - /// Whether location-scoped approvals and exact session-approved paths are cleared too. Defaults to `true`. - [JsonPropertyName("includeLocation")] - public bool? IncludeLocation { get; set; } + /// Output reservation overlapping the displayed prompt allowance plus the tokens past the effective input budget's buffer-exhaustion blocking threshold. Mirrors `SessionContextInfo.bufferTokens`. + [JsonPropertyName("bufferTokens")] + public long BufferTokens { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. + [JsonPropertyName("categories")] + public MetadataContextAttributionResultContextAttributionCategories Categories { get => field ??= new(); set; } + + /// Successful compaction history for the session. + [JsonPropertyName("compactions")] + public MetadataContextAttributionResultContextAttributionCompactions Compactions { get => field ??= new(); set; } + + /// Token count at which background compaction starts. Mirrors `SessionContextInfo.compactionThreshold`. + [JsonPropertyName("compactionThreshold")] + public long CompactionThreshold { get; set; } + + /// Flat list of per-source attribution entries. Group by `kind` and render unrecognized kinds generically. Nesting and rollups are expressed via `parentId`. + [JsonPropertyName("entries")] + public IList Entries { get => field ??= []; set; } + + /// Advertised prompt allowance for the selected context tier: the denominator for context-usage displays and capacity for `categories.freeSpace` and `categories.buffer`. Mirrors `SessionContextInfo.limit`. + [JsonPropertyName("limit")] + public long Limit { get; set; } + + /// The concrete model id the entire breakdown was tokenized against (feeds the per-model token multiplier). Under `Auto` (Free/Student) this is the resolved model, not the literal `auto` sentinel, so totals are not undercounted. A single-model approximation of a potentially multi-model Auto session. + [JsonPropertyName("modelId")] + public string ModelId { get; set; } = string.Empty; + + /// How `modelId` was chosen. Not a closed set — tolerate unknown values. Known values today: `autoResolved` (the model Auto resolved to), `selected` (the user's explicitly selected model), `default` (a fallback before any model is known). + [JsonPropertyName("modelSource")] + public string ModelSource { get; set; } = string.Empty; + + /// Effective input budget after reserving requested output against the combined context ceiling. Mirrors `SessionContextInfo.promptTokenLimit`. + [JsonPropertyName("promptTokenLimit")] + public long PromptTokenLimit { get; set; } + + /// Total token count of the current context window the entries are measured against (system message + conversation messages + tool definitions — the same total reported by /context). Divide an entry's `tokens` by this to derive its share. + [JsonPropertyName("totalTokens")] + public long TotalTokens { get; set; } } -/// Indicates whether the operation succeeded. +/// Per-source attribution breakdown for the session's current context window, or null if uninitialized. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsNotifyPromptShownResult +public sealed class MetadataContextAttributionResult { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Per-source context-window attribution, or null if the session has not yet been initialized (no system prompt or tool metadata cached). + [JsonPropertyName("contextAttribution")] + public MetadataContextAttributionResultContextAttribution? ContextAttribution { get; set; } } -/// Notification payload describing the permission prompt that the client just rendered. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionPromptShownNotification +internal sealed class SessionMetadataGetContextAttributionRequest { - /// Human-readable description of the prompt the user is being asked to approve. Used by the runtime to fire the registered `permission_prompt` notification hook (e.g. terminal bell, desktop notification). - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Snapshot of the session's recursive directory grants, exact session-approved paths, and primary working directory. +/// A single large message currently in context. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionPathsList +public sealed class ContextHeaviestMessage { - /// All directories currently allowed for tool access on this session. - [JsonPropertyName("directories")] - public IList Directories { get => field ??= []; set; } + /// Stable identifier for this message within the snapshot. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// The primary working directory for this session. - [JsonPropertyName("primary")] - public string Primary { get; set; } = string.Empty; + /// Human-readable source label, e.g. `tool: bash` or `skill: tmux`. Presentation-only. + [JsonPropertyName("label")] + public string Label { get; set; } = string.Empty; - /// Exact paths approved for this session without recursively allowing their descendants. - [JsonPropertyName("sessionApprovedPaths")] - public IList? SessionApprovedPaths { get; set; } + /// Role of the chat message (`user`, `assistant`, or `tool`). + [JsonPropertyName("role")] + public string Role { get; set; } = string.Empty; + + /// Token count currently in context for this individual message. + [JsonPropertyName("tokens")] + public long Tokens { get; set; } } -/// No parameters; returns the session's recursive directory grants and exact session-approved paths. +/// The heaviest individual messages in the session's context window, most-expensive first. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsPathsListRequest +public sealed class MetadataContextHeaviestMessagesResult +{ + /// Heaviest messages, most-expensive first. + [JsonPropertyName("messages")] + public IList Messages { get => field ??= []; set; } + + /// Total token count of the current context window, so callers can compute each message's share without a second call. + [JsonPropertyName("totalTokens")] + public long TotalTokens { get; set; } +} + +/// Parameters for the heaviest-messages query. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class MetadataContextHeaviestMessagesRequest { + /// Maximum number of messages to return, most-expensive first. Omit for the server default. + [JsonPropertyName("limit")] + public long? Limit { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the operation succeeded. +/// 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). For a local session, a report whose `cwd` diverges from the session's current working directory is ignored (the call still succeeds but records nothing and emits no event); move a local session's working directory via `metadata.setWorkingDirectory` instead. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsPathsAddResult +public sealed class MetadataRecordContextChangeResult { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } } -/// Directory path to add to the session's allowed directories. +/// Updated working-directory/git context to record on the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionPathsAddParams +internal sealed class MetadataRecordContextChangeRequest { - /// Directory to add to the allow-list. The runtime resolves and validates the path before adding, then loads conventional `.github/skills/` and `.github/agents/` definitions under it when their subsystem gates are enabled. Adding the directory is therefore also a trust decision for configuration stored there. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Updated working directory and git context. Emitted as the new payload of `session.context_changed`. + [JsonPropertyName("context")] + public SessionWorkingDirectoryContext Context { get => field ??= new(); set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the operation succeeded. +/// 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 any related side-effects (file index, etc.); it does NOT change the process working directory (a session's cwd is per-session, not process-global). For local sessions the runtime validates the target first (an absolute path that exists on disk) and re-bases the permission primary directory; a rejected validation fails the call before anything is mutated, persisted, or emitted. Location-scoped permission rules are then re-keyed to the new directory (best-effort). Remote sessions only record the path. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsPathsUpdatePrimaryResult +public sealed class MetadataSetWorkingDirectoryResult { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Working directory after the update. + [JsonPropertyName("workingDirectory")] + public string WorkingDirectory { get; set; } = string.Empty; } -/// Directory path to set as the session's new primary working directory. +/// Absolute path to set as the session's new working directory. For local sessions the path must be absolute and exist on disk: it is validated before any session state changes, and a failing validation rejects the call with nothing mutated, persisted, or emitted. Remote sessions record the path as-is. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionPathsUpdatePrimaryParams +internal sealed class MetadataSetWorkingDirectoryRequest { - /// Directory to set as the new primary working directory for the session's permission policy. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Absolute path to set as the session's working directory. The runtime updates the session's recorded cwd so subsequent operations (shell tools, file lookups, telemetry) anchor to it. + [JsonPropertyName("workingDirectory")] + public string WorkingDirectory { get; set; } = string.Empty; } -/// Indicates whether the supplied path is within the session's allowed directories. +/// 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. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionPathsAllowedCheckResult +public sealed class MetadataRecomputeContextTokensResult { - /// Whether the path is within the session's allowed directories. - [JsonPropertyName("allowed")] - public bool Allowed { get; set; } + /// Tokens contributed by user/assistant/tool messages (excludes system/developer prompts). + [JsonPropertyName("messagesTokenCount")] + public long MessagesTokenCount { get; set; } + + /// Tokens contributed by system/developer prompt snapshots. + [JsonPropertyName("systemTokenCount")] + public long SystemTokenCount { get; set; } + + /// Sum of tokens across chat-context and system-context messages currently held by the session. + [JsonPropertyName("totalTokens")] + public long TotalTokens { get; set; } } -/// Path to evaluate against the session's allowed directories. +/// Model identifier to use when re-tokenizing the session's existing messages. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionPathsAllowedCheckParams +internal sealed class MetadataRecomputeContextTokensRequest { - /// Path to check against the session's allowed directories. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Model identifier used for tokenization. The runtime token-counts both chat-context and system-context messages against this model. + [JsonPropertyName("modelId")] + public string ModelId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the supplied path is within the session's workspace directory. +/// Availability of built-in job tools surfaced to boundary consumers. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionPathsWorkspaceCheckResult +public sealed class SessionSettingsBuiltInToolAvailabilitySnapshot { - /// Whether the path is within the session workspace directory. - [JsonPropertyName("allowed")] - public bool Allowed { get; set; } + /// Whether the create-pull-request tool is available. + [JsonPropertyName("createPullRequest")] + public bool? CreatePullRequest { get; set; } + + /// Whether the report-progress tool is available. + [JsonPropertyName("reportProgress")] + public bool? ReportProgress { get; set; } } -/// Path to evaluate against the session's workspace (primary) directory. +/// Redacted job settings for a session. The job nonce is excluded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionPathsWorkspaceCheckParams +public sealed class SessionSettingsJobSnapshot { - /// Path to check against the session workspace directory. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Availability of job-specific built-in tools. + [JsonPropertyName("builtInToolAvailability")] + public SessionSettingsBuiltInToolAvailabilitySnapshot? BuiltInToolAvailability { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// GitHub Actions event type for the job. + [JsonPropertyName("eventType")] + public string? EventType { get; set; } + + /// Whether this is the workflow's trigger job. + [JsonPropertyName("isTriggerJob")] + public bool? IsTriggerJob { get; set; } } -/// Resolved location-permissions key and type. +/// Redacted model routing settings for a session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionLocationResolveResult +public sealed class SessionSettingsModelSnapshot { - /// Location key used in the location-permissions store. - [JsonPropertyName("locationKey")] - public string LocationKey { get; set; } = string.Empty; + /// Agent service callback URL for job and progress updates. + [JsonPropertyName("callbackUrl")] + public string? CallbackUrl { get; set; } - /// Whether the location is a git repo or directory. - [JsonPropertyName("locationType")] - public PermissionLocationType LocationType { get; set; } + /// Default reasoning effort for the selected model. + [JsonPropertyName("defaultReasoningEffort")] + public string? DefaultReasoningEffort { get; set; } + + /// Agent job identifier for the session. + [JsonPropertyName("instanceId")] + public string? InstanceId { get; set; } + + /// Selected model identifier. + [JsonPropertyName("model")] + public string? Model { get; set; } } -/// Working directory to resolve into a location-permissions key. +/// Online-evaluation settings safe to expose across the SDK boundary. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionLocationResolveParams +public sealed class SessionSettingsOnlineEvaluationSnapshot { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether online evaluation is disabled. + [JsonPropertyName("disableOnlineEvaluation")] + public bool? DisableOnlineEvaluation { get; set; } - /// Working directory whose permission location should be resolved. - [JsonPropertyName("workingDirectory")] - public string WorkingDirectory { get; set; } = string.Empty; + /// Whether online-evaluation output-file generation is enabled. + [JsonPropertyName("enableOnlineEvaluationOutputFile")] + public bool? EnableOnlineEvaluationOutputFile { get; set; } } -/// Summary of persisted location permissions applied to the session. +/// Redacted repository and GitHub host settings for a session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionLocationApplyResult +public sealed class SessionSettingsRepoSnapshot { - /// Number of persisted allowed directories added to the live path manager. - [JsonPropertyName("appliedDirectoryCount")] - public long AppliedDirectoryCount { get; set; } + /// Checked-out repository branch. + [JsonPropertyName("branch")] + public string? Branch { get; set; } - /// Number of location-scoped rules added to the live permission service. - [JsonPropertyName("appliedRuleCount")] - public long AppliedRuleCount { get; set; } + /// Checked-out commit SHA. + [JsonPropertyName("commit")] + public string? Commit { get; set; } - /// Location-scoped rules applied to the live permission service. - [JsonPropertyName("appliedRules")] - public IList AppliedRules { get => field ??= []; set; } + /// GitHub server host name. + [JsonPropertyName("host")] + public string? Host { get; set; } - /// Whether a different location was applied since the previous apply call. - [JsonPropertyName("changed")] - public bool Changed { get; set; } + /// Protocol used to access the GitHub host. + [JsonPropertyName("hostProtocol")] + public string? HostProtocol { get; set; } - /// Location key used in the location-permissions store. - [JsonPropertyName("locationKey")] - public string LocationKey { get; set; } = string.Empty; + /// GitHub repository database ID. + [JsonPropertyName("id")] + public double? Id { get; set; } - /// Whether the location is a git repo or directory. - [JsonPropertyName("locationType")] - public PermissionLocationType LocationType { get; set; } -} + /// Repository name. + [JsonPropertyName("name")] + public string? Name { get; set; } -/// Working directory to load persisted location permissions for. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionLocationApplyParams -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// GitHub repository owner database ID. + [JsonPropertyName("ownerId")] + public double? OwnerId { get; set; } - /// Working directory whose persisted location permissions should be applied. - [JsonPropertyName("workingDirectory")] - public string WorkingDirectory { get; set; } = string.Empty; + /// Repository owner login. + [JsonPropertyName("ownerName")] + public string? OwnerName { get; set; } + + /// Number of commits in the pull request. + [JsonPropertyName("prCommitCount")] + public double? PrCommitCount { get; set; } + + /// Whether the repository is writable. + [JsonPropertyName("readWrite")] + public bool? ReadWrite { get; set; } + + /// GitHub secret-scanning service URL. + [JsonPropertyName("secretScanningUrl")] + public string? SecretScanningUrl { get; set; } + + /// GitHub server base URL. + [JsonPropertyName("serverUrl")] + public string? ServerUrl { get; set; } } -/// Indicates whether the operation succeeded. +/// Redacted validation and memory-tool settings for a session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsLocationsAddToolApprovalResult +public sealed class SessionSettingsValidationSnapshot { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Whether advisory validation is enabled. + [JsonPropertyName("advisoryEnabled")] + public bool? AdvisoryEnabled { get; set; } + + /// Whether CodeQL validation is enabled. + [JsonPropertyName("codeqlEnabled")] + public bool? CodeqlEnabled { get; set; } + + /// Whether code-review validation is enabled. + [JsonPropertyName("codeReviewEnabled")] + public bool? CodeReviewEnabled { get; set; } + + /// Model used for code-review validation. + [JsonPropertyName("codeReviewModel")] + public string? CodeReviewModel { get; set; } + + /// Dependabot validation timeout budget in seconds. + [JsonPropertyName("dependabotTimeout")] + public double? DependabotTimeout { get; set; } + + /// Whether the memory-store tool is enabled. + [JsonPropertyName("memoryStoreEnabled")] + public bool? MemoryStoreEnabled { get; set; } + + /// Whether the memory-vote tool is enabled. + [JsonPropertyName("memoryVoteEnabled")] + public bool? MemoryVoteEnabled { get; set; } + + /// Whether secret-scanning validation is enabled. + [JsonPropertyName("secretScanningEnabled")] + public bool? SecretScanningEnabled { get; set; } + + /// General validation timeout budget in seconds. + [JsonPropertyName("timeout")] + public double? Timeout { get; set; } } -/// Tool approval to persist and apply. -/// Polymorphic base type discriminated by kind. +/// Redacted, serializable view of session runtime settings for SDK boundary consumers. Secrets and raw feature flags are intentionally excluded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsCommands), "commands")] -[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsRead), "read")] -[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsWrite), "write")] -[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsMcp), "mcp")] -[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsMcpSampling), "mcp-sampling")] -[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsMemory), "memory")] -[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsCustomTool), "custom-tool")] -[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsExtensionManagement), "extension-management")] -[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsWorkflow), "workflow")] -[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess), "extension-permission-access")] -[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsExtensionEnvAccess), "extension-env-access")] -public partial class PermissionsLocationsAddToolApprovalDetails +internal sealed class SessionSettingsSnapshot { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// Name of the SDK client that created the session. + [JsonPropertyName("clientName")] + public string? ClientName { get; set; } + /// Redacted job settings. + [JsonPropertyName("job")] + public SessionSettingsJobSnapshot Job { get => field ??= new(); set; } -/// Location-persisted tool approval details for specific command identifiers. -/// The commands variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsCommands : PermissionsLocationsAddToolApprovalDetails -{ - /// - [JsonIgnore] - public override string Kind => "commands"; + /// Redacted model routing settings. + [JsonPropertyName("model")] + public SessionSettingsModelSnapshot Model { get => field ??= new(); set; } - /// Command identifiers covered by this approval. - [JsonPropertyName("commandIdentifiers")] - public required IList CommandIdentifiers { get; set; } + /// Online-evaluation settings safe for SDK consumers. + [JsonPropertyName("onlineEvaluation")] + public SessionSettingsOnlineEvaluationSnapshot OnlineEvaluation { get => field ??= new(); set; } + + /// Redacted repository and host settings. + [JsonPropertyName("repo")] + public SessionSettingsRepoSnapshot Repo { get => field ??= new(); set; } + + /// Session start time as Unix epoch milliseconds. + [JsonPropertyName("startTimeMs")] + public double? StartTimeMs { get; set; } + + /// Session timeout in milliseconds. + [JsonPropertyName("timeoutMs")] + public double? TimeoutMs { get; set; } + + /// Redacted validation and memory-tool settings. + [JsonPropertyName("validation")] + public SessionSettingsValidationSnapshot Validation { get => field ??= new(); set; } + + /// Agent runtime version selector copied from the session settings, such as `latest` or a runtime release identifier. + [JsonPropertyName("version")] + public string? Version { get; set; } } -/// Location-persisted tool approval details for read-only filesystem operations. -/// The read variant of . +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsRead : PermissionsLocationsAddToolApprovalDetails +internal sealed class SessionSettingsSnapshotRequest { - /// - [JsonIgnore] - public override string Kind => "read"; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Location-persisted tool approval details for filesystem write operations. -/// The write variant of . +/// Result of evaluating a Rust-owned settings predicate. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsWrite : PermissionsLocationsAddToolApprovalDetails +internal sealed class SessionSettingsEvaluatePredicateResult { - /// - [JsonIgnore] - public override string Kind => "write"; + /// Whether the named settings predicate evaluated to enabled. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } } -/// Location-persisted tool approval details for an MCP server tool, or all tools when `toolName` is null. -/// The mcp variant of . +/// Named Rust-owned settings predicate to evaluate for this session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsMcp : PermissionsLocationsAddToolApprovalDetails +internal sealed class SessionSettingsEvaluatePredicateRequest { - /// - [JsonIgnore] - public override string Kind => "mcp"; + /// Predicate name. The runtime owns the raw feature-flag names and composition logic. + [JsonPropertyName("name")] + public SessionSettingsPredicateName Name { get; set; } - /// MCP server name. - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// MCP tool name, or null to cover every tool on the server. + /// Tool name for tool-scoped predicates such as trivial-change handling. [JsonPropertyName("toolName")] public string? ToolName { get; set; } } -/// Location-persisted tool approval details for MCP sampling requests from a server. -/// The mcp-sampling variant of . +/// Content-exclusion decision for one requested path. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsMcpSampling : PermissionsLocationsAddToolApprovalDetails +public sealed class ContentExclusionPathCheck { - /// - [JsonIgnore] - public override string Kind => "mcp-sampling"; + /// Whether the session's complete content-exclusion policy excludes the path. + [JsonPropertyName("excluded")] + public bool Excluded { get; set; } - /// MCP server name. - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } + /// The path supplied by the caller. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; } -/// Location-persisted tool approval details for writes to long-term memory. -/// The memory variant of . +/// Batch content-exclusion result. Callers must fail closed when policy evaluation is unavailable. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsMemory : PermissionsLocationsAddToolApprovalDetails +public sealed class ContentExclusionCheckPathsResult { - /// - [JsonIgnore] - public override string Kind => "memory"; + /// Whether the session's policy service was available for the complete batch. When false, checks is empty and callers must treat every requested path as excluded. + [JsonPropertyName("available")] + public bool Available { get; set; } + + /// Per-path decisions in request order. Empty when available is false. + [JsonPropertyName("checks")] + public IList Checks { get => field ??= []; set; } } -/// Location-persisted tool approval details for a custom tool, keyed by tool name. -/// The custom-tool variant of . +/// Local file system absolute paths within the session working directory to check against its content-exclusion policy. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsCustomTool : PermissionsLocationsAddToolApprovalDetails +internal sealed class ContentExclusionCheckPathsRequest { - /// - [JsonIgnore] - public override string Kind => "custom-tool"; + /// Local file system absolute paths within the session working directory to check. Results are returned in the same order, including duplicates. + [JsonPropertyName("paths")] + public IList Paths { get => field ??= []; set; } - /// Custom tool name. - [JsonPropertyName("toolName")] - public required string ToolName { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Location-persisted tool approval details for extension-management operations, optionally narrowed by operation. -/// The extension-management variant of . +/// Identifier of the spawned shell process, usable with shell.kill while the process is running. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsExtensionManagement : PermissionsLocationsAddToolApprovalDetails +public sealed class ShellExecResult { - /// - [JsonIgnore] - public override string Kind => "extension-management"; - - /// Optional operation identifier; when omitted, the approval covers all extension management operations. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("operation")] - public string? Operation { get; set; } + /// Identifier usable with shell.kill while the process is running. + [JsonPropertyName("processId")] + public string ProcessId { get; set; } = string.Empty; } -/// Location-persisted workflow approval, optionally narrowed by approval key. -/// The workflow variant of . +/// Shell command to run, with optional working directory and timeout in milliseconds. Spawn failures return an RPC error. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsWorkflow : PermissionsLocationsAddToolApprovalDetails +internal sealed class ShellExecRequest { - /// - [JsonIgnore] - public override string Kind => "workflow"; + /// Shell command to execute. + [JsonPropertyName("command")] + public string Command { get; set; } = string.Empty; - /// Optional workflow operation name or canonical approval key; when omitted, the approval covers all workflow operations. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("approvalKey")] - public string? ApprovalKey { get; set; } + /// Working directory (defaults to session working directory). + [JsonPropertyName("cwd")] + public string? Cwd { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; + + /// Timeout in milliseconds (default: 30000). + [JsonConverter(typeof(MillisecondsTimeSpanConverter))] + [JsonPropertyName("timeout")] + public TimeSpan? Timeout { get; set; } } -/// Location-persisted tool approval details for an extension's permission-gated capability access, keyed by extension name. -/// The extension-permission-access variant of . +/// Indicates whether the signal was delivered; false if the process was unknown or already exited. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess : PermissionsLocationsAddToolApprovalDetails +public sealed class ShellKillResult { - /// - [JsonIgnore] - public override string Kind => "extension-permission-access"; + /// Whether the signal was sent successfully. + [JsonPropertyName("killed")] + public bool Killed { get; set; } +} - /// Extension name. - [JsonPropertyName("extensionName")] - public required string ExtensionName { get; set; } +/// Identifier of a process previously returned by "shell.exec" and the signal to send. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ShellKillRequest +{ + /// Process identifier returned by shell.exec. + [JsonPropertyName("processId")] + public string ProcessId { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; + + /// Signal to send (default: SIGTERM). + [JsonPropertyName("signal")] + public ShellKillSignal? Signal { get; set; } } -/// Location-persisted tool approval details for an extension's access to sensitive environment variables, keyed by extension name and the exact set of variable names. -/// The extension-env-access variant of . +/// Result of a user-requested shell command. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsExtensionEnvAccess : PermissionsLocationsAddToolApprovalDetails +public sealed class UserRequestedShellCommandResult { - /// - [JsonIgnore] - public override string Kind => "extension-env-access"; + /// Error output when the execution failed. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Names of the sensitive environment variables this approval covers. Values are never persisted. - [JsonPropertyName("environmentVariables")] - public required IList EnvironmentVariables { get; set; } + /// Process exit code, when available. + [JsonPropertyName("exitCode")] + public long? ExitCode { get; set; } - /// Extension name. - [JsonPropertyName("extensionName")] - public required string ExtensionName { get; set; } + /// Captured command output. + [JsonPropertyName("output")] + public string Output { get; set; } = string.Empty; + + /// Whether the command completed successfully. + [JsonPropertyName("success")] + public bool Success { get; set; } + + /// Tool call id emitted for the shell execution. + [JsonPropertyName("toolCallId")] + public string ToolCallId { get; set; } = string.Empty; } -/// Location-scoped tool approval to persist. +/// User-requested shell command and cancellation handle. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionLocationAddToolApprovalParams +internal sealed class ShellExecuteUserRequestedRequest { - /// Tool approval to persist and apply. - [JsonPropertyName("approval")] - public PermissionsLocationsAddToolApprovalDetails Approval { get => field ??= new(); set; } + /// Shell command to execute. + [JsonPropertyName("command")] + public string Command { get; set; } = string.Empty; - /// Location key (git root or cwd) to persist the approval to. - [JsonPropertyName("locationKey")] - public string LocationKey { get; set; } = string.Empty; + /// Caller-provided cancellation handle for this execution. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Folder trust check result. +/// Cancellation result for a user-requested shell command. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class FolderTrustCheckResult +public sealed class CancelUserRequestedShellCommandResult { - /// Whether the folder is trusted. - [JsonPropertyName("trusted")] - public bool Trusted { get; set; } + /// Whether an in-flight execution was found and signalled to cancel. + [JsonPropertyName("cancelled")] + public bool Cancelled { get; set; } } -/// Folder path to check for trust. +/// User-requested shell execution cancellation handle. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class FolderTrustCheckParams +internal sealed class ShellCancelUserRequestedRequest { - /// Folder path to check. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Request ID previously passed to executeUserRequested. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the operation succeeded. +/// Post-compaction context window usage breakdown. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsFolderTrustAddTrustedResult +public sealed class HistoryCompactContextWindow { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Token count from non-system messages (user, assistant, tool). + [JsonPropertyName("conversationTokens")] + public long? ConversationTokens { get; set; } + + /// Current total tokens in the context window (system + conversation + tool definitions). + [JsonPropertyName("currentTokens")] + public long CurrentTokens { get; set; } + + /// Current number of messages in the conversation. + [JsonPropertyName("messagesLength")] + public long MessagesLength { get; set; } + + /// Token count from system message(s). + [JsonPropertyName("systemTokens")] + public long? SystemTokens { get; set; } + + /// Maximum token count for the model's context window. + [JsonPropertyName("tokenLimit")] + public long TokenLimit { get; set; } + + /// Token count from tool definitions. + [JsonPropertyName("toolDefinitionsTokens")] + public long? ToolDefinitionsTokens { get; set; } } -/// Folder path to add to trusted folders. +/// Compaction outcome with the number of tokens and messages removed, summary text, and the resulting context window breakdown. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class FolderTrustAddParams +public sealed class HistoryCompactResult { - /// Folder path to mark as trusted. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Post-compaction context window usage breakdown. + [JsonPropertyName("contextWindow")] + public HistoryCompactContextWindow? ContextWindow { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Number of messages removed during compaction. + [JsonPropertyName("messagesRemoved")] + public long MessagesRemoved { get; set; } + + /// Whether compaction completed successfully. + [JsonPropertyName("success")] + public bool Success { get; set; } + + /// Summary text produced by compaction. Omitted when compaction did not produce a summary (e.g. failure path). + [JsonPropertyName("summaryContent")] + public string? SummaryContent { get; set; } + + /// Number of tokens freed by compaction. + [JsonPropertyName("tokensRemoved")] + public long TokensRemoved { get; set; } } -/// Indicates whether the operation succeeded. +/// RPC data type for SessionHistoryCompact operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsUrlsSetUnrestrictedModeResult +public sealed class SessionHistoryCompactRequest { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Optional user-provided instructions to focus the compaction summary. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(4000)] + [JsonPropertyName("customInstructions")] + public string? CustomInstructions { get; set; } + + /// Context window token limit this compaction is targeting, recorded as the `tokenLimit` on the persisted `session.compaction_start` / `session.compaction_complete` events. Set it when the compaction targets a window other than the compacting model's own, e.g. switching to a model with a smaller context window: the compaction still runs on the current model, so the limit that motivated it would otherwise be lost. When absent, the events record the compacting model's own resolved limit. Attribution metadata only - it does not change how much the compaction removes. + [JsonPropertyName("tokenLimit")] + public long? TokenLimit { get; set; } + + /// What initiated this compaction request, recorded as the `trigger` on the persisted `session.compaction_start` / `session.compaction_complete` events. When absent, the compaction is persisted without trigger attribution (initiator unknown). + [JsonPropertyName("trigger")] + public SessionHistoryCompactRequestTrigger? Trigger { get; set; } } -/// Whether the URL-permission policy should run in unrestricted mode. +/// RPC data type for SessionHistoryCompactRequestWithSession operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionUrlsSetUnrestrictedModeParams +internal sealed class SessionHistoryCompactRequestWithSession { - /// Whether to allow access to all URLs without prompting. Toggles the runtime's URL-permission policy in place. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } + /// Optional user-provided instructions to focus the compaction summary. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(4000)] + [JsonPropertyName("customInstructions")] + public string? CustomInstructions { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Context window token limit this compaction is targeting, recorded as the `tokenLimit` on the persisted `session.compaction_start` / `session.compaction_complete` events. Set it when the compaction targets a window other than the compacting model's own, e.g. switching to a model with a smaller context window: the compaction still runs on the current model, so the limit that motivated it would otherwise be lost. When absent, the events record the compacting model's own resolved limit. Attribution metadata only - it does not change how much the compaction removes. + [JsonPropertyName("tokenLimit")] + public long? TokenLimit { get; set; } + + /// What initiated this compaction request, recorded as the `trigger` on the persisted `session.compaction_start` / `session.compaction_complete` events. When absent, the compaction is persisted without trigger attribution (initiator unknown). + [JsonPropertyName("trigger")] + public SessionHistoryCompactRequestTrigger? Trigger { get; set; } } -/// The repository the remote session targets. +/// Number of events that were removed by the truncation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataSnapshotRemoteMetadataRepository +public sealed class HistoryTruncateResult { - /// The branch the remote session is operating on. - [JsonPropertyName("branch")] - public string Branch { get; set; } = string.Empty; + /// Failure detail when checkpointCleanupFailed is true. + [JsonPropertyName("checkpointCleanupError")] + public string? CheckpointCleanupError { get; set; } - /// The GitHub repository name (without owner). - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// True when conversation truncation succeeded but post-truncation workspace checkpoint cleanup failed. History is already truncated; callers may still prune snapshots but should report a checkpoint-cleanup rather than a truncation failure. + [JsonPropertyName("checkpointCleanupFailed")] + public bool? CheckpointCleanupFailed { get; set; } - /// The GitHub owner (user or organization) of the target repository. - [JsonPropertyName("owner")] - public string Owner { get; set; } = string.Empty; + /// Number of events that were removed. + [JsonPropertyName("eventsRemoved")] + public long EventsRemoved { get; set; } } -/// Remote-session-specific metadata. Populated only when `isRemote` is true. Fields are immutable for the lifetime of the session. +/// Identifier of the event to truncate to; this event and all later events are removed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataSnapshotRemoteMetadata +internal sealed class HistoryTruncateRequest { - /// The pull request number the remote session is associated with, if any. - [JsonPropertyName("pullRequestNumber")] - public long? PullRequestNumber { get; set; } - - /// The repository the remote session targets. - [JsonPropertyName("repository")] - public MetadataSnapshotRemoteMetadataRepository Repository { get => field ??= new(); set; } - - /// The original resource identifier (task ID or PR node ID), preserved across event-replay reconstructions. Falls back to `sessionId` when absent. - [JsonPropertyName("resourceId")] - public string? ResourceId { get; set; } + /// Event ID to truncate to. This event and all events after it are removed from the session. + [JsonPropertyName("eventId")] + public string EventId { get; set; } = string.Empty; - /// Whether the remote task originated from Copilot Coding Agent (cca) or a CLI `--remote` invocation. - [JsonPropertyName("taskType")] - public MetadataSnapshotRemoteMetadataTaskType? TaskType { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Public-facing projection of workspace metadata for SDK / TUI consumers. -public sealed class SessionMetadataSnapshotWorkspace +/// A root user turn that the session can rewind to. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class HistoryRewindPoint { - /// Branch checked out at session start, if any. - [JsonPropertyName("branch")] - public string? Branch { get; set; } - - /// ISO 8601 timestamp when the workspace was created. - [JsonPropertyName("created_at")] - public DateTimeOffset? CreatedAt { get; set; } + /// Whether at least one file in this turn or a later turn can be restored. + [JsonPropertyName("canRestoreFiles")] + public bool CanRestoreFiles { get; set; } - /// Current working directory at session start. - [JsonPropertyName("cwd")] - public string? Cwd { get; set; } + /// ID of the user.message event that begins the discarded suffix. + [JsonPropertyName("eventId")] + public string EventId { get; set; } = string.Empty; - /// Resolved git root for cwd, if any. - [JsonPropertyName("git_root")] - public string? GitRoot { get; set; } + /// Number of unique files in this turn and all later turns that have captured changes. + [JsonPropertyName("fileCount")] + public long FileCount { get; set; } - /// Repository host type, if known. - [JsonPropertyName("host_type")] - public WorkspaceSummaryHostType? HostType { get; set; } + /// Whether this turn was an automatically injected autopilot continuation. + [JsonPropertyName("isAutopilotContinuation")] + public bool IsAutopilotContinuation { get; set; } - /// Workspace identifier (1:1 with sessionId). - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Lines added by this turn's captured file changes. + [JsonPropertyName("linesAdded")] + public long LinesAdded { get; set; } - /// Display name for the session, if set. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// Lines removed by this turn's captured file changes. + [JsonPropertyName("linesRemoved")] + public long LinesRemoved { get; set; } - /// Repository identifier in 'owner/repo' or 'org/project/repo' format, if any. - [JsonPropertyName("repository")] - public string? Repository { get; set; } + /// ISO timestamp of the user turn. + [JsonPropertyName("timestamp")] + public string Timestamp { get; set; } = string.Empty; - /// ISO 8601 timestamp when the workspace was last updated. - [JsonPropertyName("updated_at")] - public DateTimeOffset? UpdatedAt { get; set; } + /// Whether this turn itself captured any file changes. + [JsonPropertyName("turnChangedFiles")] + public bool TurnChangedFiles { get; set; } - /// Whether the display name was explicitly set by the user. - [JsonPropertyName("user_named")] - public bool? UserNamed { get; set; } + /// User-visible message text for the turn. + [JsonPropertyName("userMessage")] + public string UserMessage { get; set; } = string.Empty; } -/// Point-in-time snapshot of slow-changing session identifier and state fields. +/// Rewind points and file-change-tracking availability for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionMetadataSnapshot +public sealed class HistoryListRewindPointsResult { - /// True when the session was detected to be in use by another process at construction time. Local consumers may surface a confirmation prompt before fully attaching. Always false for new sessions. - [JsonPropertyName("alreadyInUse")] - public bool AlreadyInUse { get; set; } - - /// Runtime client name associated with the session (telemetry identifier). - [JsonPropertyName("clientName")] - public string? ClientName { get; set; } - - /// The current agent mode for this session (e.g., 'interactive', 'plan', 'autopilot'). - [JsonPropertyName("currentMode")] - public MetadataSnapshotCurrentMode CurrentMode { get; set; } - - /// Live indexed-search state for this session activation. Omitted by runtimes that do not expose indexed-search status; absence does not indicate enablement. - [JsonPropertyName("indexedSearch")] - public IndexedSearchState? IndexedSearch { get; set; } - - /// User-provided name supplied at session construction (via `--name`), if any. Immutable after construction. - [JsonPropertyName("initialName")] - public string? InitialName { get; set; } - - /// Whether this is a remote session (i.e., one whose runtime executes elsewhere and is steered through this process). - [JsonPropertyName("isRemote")] - public bool IsRemote { get; set; } - - /// ISO 8601 timestamp of when the session's persisted state was last modified on disk. For new sessions, equals startTime. For resumed sessions, reflects the previous modification time at construction. - [JsonPropertyName("modifiedTime")] - public DateTimeOffset ModifiedTime { get; set; } + /// Whether this session captured file changes from its first turn. + [JsonPropertyName("fileChangeTrackingEnabled")] + public bool FileChangeTrackingEnabled { get; set; } - /// Remote-session-specific metadata. Populated only when `isRemote` is true. Fields are immutable for the lifetime of the session. - [JsonPropertyName("remoteMetadata")] - public MetadataSnapshotRemoteMetadata? RemoteMetadata { get; set; } + /// Root user turns in chronological order. Empty when `unavailableReason` is set. + [JsonPropertyName("points")] + public IList Points { get => field ??= []; set; } - /// Currently selected model identifier, if any. - [JsonPropertyName("selectedModel")] - public string? SelectedModel { get; set; } + /// Why the listed points could not be produced, when applicable; the points list is empty whenever it is set. `unsupported-remote-session` is permanent for the session and comes with `fileChangeTrackingEnabled: false`. `session-busy` is transient and only ever reported by a session that *is* tracking (`fileChangeTrackingEnabled: true`), because the file-change captures cannot be read while work that may still mutate them is in flight; the same request succeeds once the session settles, so a client that wants points should retry rather than treat it as a failure. It is never `file-change-tracking-disabled`: an untracked local session still lists conversation-only points and reports that through `fileChangeTrackingEnabled: false`. + [JsonPropertyName("unavailableReason")] + public HistoryRewindUnavailableReason? UnavailableReason { get; set; } +} - /// The unique identifier of the session. +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionHistoryListRewindPointsRequest +{ + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; +} - /// Current session limits, or null when no limits are active. - [JsonPropertyName("sessionLimits")] - public SessionLimitsConfig? SessionLimits { get; set; } +/// A file that a conversation-and-files rewind would restore. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class HistoryRewindFilePreview +{ + /// Aggregate change made across the discarded turns. + [JsonPropertyName("changeType")] + public HistoryRewindChangeType ChangeType { get; set; } - /// ISO 8601 timestamp of when the session started. - [JsonPropertyName("startTime")] - public DateTimeOffset StartTime { get; set; } + /// Lines added across the discarded turns. + [JsonPropertyName("linesAdded")] + public long LinesAdded { get; set; } - /// Short human-readable summary of the session, if known. Omitted when no summary has been generated. - [JsonPropertyName("summary")] - public string? Summary { get; set; } + /// Lines removed across the discarded turns. + [JsonPropertyName("linesRemoved")] + public long LinesRemoved { get; set; } - /// Absolute path to the session's current working directory. - [JsonPropertyName("workingDirectory")] - public string WorkingDirectory { get; set; } = string.Empty; + /// Absolute path of the captured file. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; +} - /// Public-facing workspace metadata for this session, or null if the session has no associated workspace. Excludes runtime-internal fields (GitHub IDs, summary count, internal flags). - [JsonPropertyName("workspace")] - public SessionMetadataSnapshotWorkspace? Workspace { get; set; } +/// Files and aggregate changes for a prospective rewind. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class HistoryPreviewRewindResult +{ + /// Whether file restore is available for this session. This is authoritative: switch on it and read `reason` only when it is false. + [JsonPropertyName("available")] + public bool Available { get; set; } - /// Absolute path to the session's workspace directory on disk, or null if the session has no associated workspace. - [JsonPropertyName("workspacePath")] - public string? WorkspacePath { get; set; } + /// Number of unique files in the preview. + [JsonPropertyName("fileCount")] + public long FileCount { get; set; } + + /// Files ordered by path. + [JsonPropertyName("files")] + public IList Files { get => field ??= []; set; } + + /// Why file restore is unavailable, when applicable. Populated only when `available` is false and never set when `available` is true. + [JsonPropertyName("reason")] + public HistoryRewindUnavailableReason? Reason { get; set; } } -/// Identifies the target session. +/// Event boundary to preview for conversation-and-files rewind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMetadataSnapshotRequest +internal sealed class HistoryPreviewRewindRequest { + /// ID of the user.message event that begins the discarded suffix. + [JsonPropertyName("eventId")] + public string EventId { get; set; } = string.Empty; + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Identifies the target session. +/// A captured file that rewind intentionally left unchanged. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMetadataGetClientMetadataRequest +public sealed class HistorySkippedFileRestore { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Absolute path of the skipped file. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + + /// Reason the file was not restored. + [JsonPropertyName("reason")] + public HistoryFileRestoreSkipReason Reason { get; set; } } -/// Atomic patch for client-owned session metadata. Operations apply in clear, remove, then set order. The resulting bag must satisfy the ClientMetadata entry and serialized-size limits. Local storage coordinates concurrent runtime processes; custom SessionFs providers must serialize writers that access the same session from multiple processes. +/// Structured outcome of a rewind request. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class MetadataUpdateClientMetadataRequest +public sealed class HistoryRewindResult { - /// Remove every existing client metadata entry before applying remove and set. Defaults to false. - [JsonPropertyName("clear")] - public bool? Clear { get; set; } + /// Failure detail. Set only for the failure and partial-failure outcomes (`files-rolled-back`, `rollback-incomplete`, `truncation-failed`, `checkpoint-cleanup-failed`, `snapshot-prune-failed`); omitted for `success` and for the unavailable outcomes (`session-busy`, `file-change-tracking-disabled`, `unsupported-remote-session`). + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Case-sensitive keys to remove. Missing keys are ignored. Each key must be non-empty, at most 256 UTF-8 bytes, and outside the reserved `copilot/` and `github/` namespaces. - [JsonPropertyName("remove")] - public IList? Remove { get; set; } + /// Number of persisted events removed by conversation truncation. Present only when truncation succeeded (outcomes `success`, `checkpoint-cleanup-failed`, and `snapshot-prune-failed`); omitted for every unavailable outcome (`session-busy`, `file-change-tracking-disabled`, `unsupported-remote-session`) and for `truncation-failed`, `files-rolled-back`, and `rollback-incomplete`. + [JsonPropertyName("eventsRemoved")] + public long? EventsRemoved { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Overall rewind outcome. This discriminates the result: it governs which of the remaining fields are populated, so consumers must switch on it before reading `eventsRemoved`, `restoredFiles`, `skippedFiles`, or `error`. See each field for the outcomes that populate it. + [JsonPropertyName("outcome")] + public HistoryRewindOutcome Outcome { get; set; } - /// String entries to add or replace. Set wins when a key also appears in remove. Each key must be non-empty, at most 256 UTF-8 bytes, and outside the reserved `copilot/` and `github/` namespaces. Each value may contain at most 16 KiB of UTF-8 data. - [JsonPropertyName("set")] - public IDictionary? Set { get; set; } -} + /// Absolute paths restored to their captured preimages. Always empty for conversation-only rewinds and for the unavailable outcomes (`session-busy`, `file-change-tracking-disabled`, `unsupported-remote-session`); only conversation-and-files outcomes that reached the file-restore stage populate it. + [JsonPropertyName("restoredFiles")] + public IList RestoredFiles { get => field ??= []; set; } -/// Indicates whether the local session is currently processing a turn or background continuation. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataIsProcessingResult -{ - /// Whether the session is currently processing user/agent messages. False for non-local sessions (which don't run a local agentic loop). Reflects an in-flight turn or background continuation. - [JsonPropertyName("processing")] - public bool Processing { get; set; } + /// Captured files intentionally left unchanged. Always empty for conversation-only rewinds and for the unavailable outcomes (`session-busy`, `file-change-tracking-disabled`, `unsupported-remote-session`); only conversation-and-files outcomes that reached the file-restore stage populate it. + [JsonPropertyName("skippedFiles")] + public IList SkippedFiles { get => field ??= []; set; } } -/// Identifies the target session. +/// Boundary and mode for rewinding session history. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMetadataIsProcessingRequest +internal sealed class HistoryRewindRequest { + /// ID of the user.message event that begins the discarded suffix. + [JsonPropertyName("eventId")] + public string EventId { get; set; } = string.Empty; + + /// Whether to rewind only conversation history or also restore captured files. + [JsonPropertyName("mode")] + public HistoryRewindMode Mode { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Current activity flags for the session. +/// Indicates whether an in-progress background compaction was cancelled. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionActivity +public sealed class HistoryCancelBackgroundCompactionResult { - /// Whether an in-flight operation can currently be aborted. - [JsonPropertyName("abortable")] - public bool Abortable { get; set; } - - /// Whether the session currently has active work, including running turns or tasks. - [JsonPropertyName("hasActiveWork")] - public bool HasActiveWork { get; set; } + /// Whether an in-progress background compaction was cancelled. False when no compaction was running, when the session is remote, or when the underlying processor was unavailable. + [JsonPropertyName("cancelled")] + public bool Cancelled { get; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMetadataActivityRequest +internal sealed class SessionHistoryCancelBackgroundCompactionRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Token-usage breakdown for the session's current context window. -public sealed class MetadataContextInfoResultContextInfo +/// Indicates whether an in-progress manual compaction was aborted. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class HistoryAbortManualCompactionResult { - /// Output reservation overlapping the displayed prompt allowance plus tokens after the effective input budget's buffer-exhaustion blocking threshold (default 95%). - [JsonPropertyName("bufferTokens")] - public long BufferTokens { get; set; } - - /// Token count at which background compaction starts (configurable percentage of promptTokenLimit). - [JsonPropertyName("compactionThreshold")] - public long CompactionThreshold { get; set; } - - /// Tokens consumed by user/assistant/tool messages. - [JsonPropertyName("conversationTokens")] - public long ConversationTokens { get; set; } - - /// Provider-owned model display label, or the Auto routing label. Presentation only; modelName remains the tokenization identity. - [JsonPropertyName("displayModelName")] - public string? DisplayModelName { get; set; } - - /// Advertised prompt allowance for the selected context tier, without adding output tokens. The denominator for context-usage displays. - [JsonPropertyName("limit")] - public long Limit { get; set; } - - /// Tokens consumed by MCP tool definitions (subset of toolDefinitionsTokens, excludes deferred tools). - [JsonPropertyName("mcpToolsTokens")] - public long McpToolsTokens { get; set; } - - /// The model used for token counting. - [JsonPropertyName("modelName")] - public string ModelName { get; set; } = string.Empty; - - /// Effective input budget: the selected tier's prompt allowance bounded by the combined context ceiling minus the requested output allowance. Uses DEFAULT_TOKEN_LIMIT when limits are unspecified. - [JsonPropertyName("promptTokenLimit")] - public long PromptTokenLimit { get; set; } - - /// Provider whose catalog supplies this context window. Omitted when provider identity is unavailable. - [JsonPropertyName("provider")] - public ModelProviderRef? Provider { get; set; } - - /// Tokens consumed by the system prompt. - [JsonPropertyName("systemTokens")] - public long SystemTokens { get; set; } - - /// Tokens consumed by tool definitions sent to the model (excludes deferred tools). - [JsonPropertyName("toolDefinitionsTokens")] - public long ToolDefinitionsTokens { get; set; } - - /// Sum of system, conversation and tool-definition tokens. - [JsonPropertyName("totalTokens")] - public long TotalTokens { get; set; } + /// Whether an in-progress manual compaction was aborted. False when no manual compaction was running, when its abort controller was already aborted, or when the session is remote. + [JsonPropertyName("aborted")] + public bool Aborted { get; set; } } -/// Token breakdown for the session's current context window, or null if uninitialized. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataContextInfoResult +internal sealed class SessionHistoryAbortManualCompactionRequest { - /// Token breakdown for the current context window, or null if the session has not yet been initialized (no system prompt or tool metadata cached). - [JsonPropertyName("contextInfo")] - public MetadataContextInfoResultContextInfo? ContextInfo { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Model identifier and token limits used to compute the context-info breakdown. +/// Markdown summary of the conversation context (empty when not available). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class MetadataContextInfoRequest -{ - /// Requested output allowance to reserve against the combined context ceiling. Pass 0 to resolve the session's request cap, falling back to the model's advertised output limit. - [JsonPropertyName("outputTokenLimit")] - public long OutputTokenLimit { get; set; } - - /// Advertised prompt allowance. Pass 0 to resolve the selected model and context tier from the session. - [JsonPropertyName("promptTokenLimit")] - public long PromptTokenLimit { get; set; } - - /// Provider owning selectedModel. Omit to use the current provider for the current model, or deterministic legacy lookup for a different model. - [JsonPropertyName("providerId")] - public string? ProviderId { get; set; } - - /// Model identifier used for tokenization. Omit to use the session default. Used both for token counting and to compute display values. - [JsonPropertyName("selectedModel")] - public string? SelectedModel { get; set; } +public sealed class HistorySummarizeForHandoffResult +{ + /// Markdown summary of the conversation context produced by an LLM. Empty string when there are no messages or when the session does not support local summarization. + [JsonPropertyName("summary")] + public string Summary { get; set; } = string.Empty; +} +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionHistorySummarizeForHandoffRequest +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. -public sealed class MetadataContextAttributionResultContextAttributionCategories +/// What a successful clear removed. A clear that could not be applied rejects instead of reporting a count. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class HistoryClearContextResult { - /// Overlapping output reservation plus post-blocking-threshold buffer. - [JsonPropertyName("buffer")] - public long Buffer { get; set; } - - /// Custom-instructions tokens (0 when none are configured). - [JsonPropertyName("customInstructions")] - public long CustomInstructions { get; set; } - - /// Remaining unused window capacity (clamped at 0). - [JsonPropertyName("freeSpace")] - public long FreeSpace { get; set; } - - /// MCP tool-definition tokens. - [JsonPropertyName("mcpTools")] - public long McpTools { get; set; } - - /// Conversation (user/assistant/tool) message tokens. - [JsonPropertyName("messages")] - public long Messages { get; set; } - - /// System prompt tokens, excluding custom instructions. - [JsonPropertyName("systemPrompt")] - public long SystemPrompt { get; set; } - - /// Non-MCP tool-definition tokens. - [JsonPropertyName("systemTools")] - public long SystemTools { get; set; } + /// Number of non-system, non-developer messages that were removed from the conversation. Zero only when the window already held no conversation. + [JsonPropertyName("messagesCleared")] + public long MessagesCleared { get; set; } } -/// Successful compaction history for the session. -public sealed class MetadataContextAttributionResultContextAttributionCompactions +/// Parameters for clearing the conversation and seeding the window that replaces it. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class HistoryClearContextRequest { - /// Number of successful compactions in this session. - [JsonPropertyName("count")] - public long Count { get; set; } + /// First user message of the fresh context window. Required: a cleared window holding only system and developer messages is not a conversation a model can answer, so every clear seeds the window it creates. Delivered by the enclosing turn driver once the agentic loop exits, which is why the call must be made from inside a tool handler. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// RPC data type for MetadataContextAttributionResultContextAttributionEntry operations. -public sealed class MetadataContextAttributionResultContextAttributionEntry +/// User-facing pending queue entry, with kind and display text for a queued message, slash command, or model change. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class QueuePendingItems { - /// Supplementary per-entry metadata (e.g. `messageCount`, `role`, `evictable`, `pluginSource`). Values are stringified; parse as needed and ignore unrecognized keys. - [JsonPropertyName("attributes")] - public IDictionary? Attributes { get; set; } + /// Agent mode stored on this queued entry, as stamped when it was enqueued. Items without an explicit mode report interactive. This is not necessarily the mode that will constrain the turn: a plan or autopilot session applies its own write gate, continuation loop and permission posture to every drained item regardless of the mode stored here. + [JsonPropertyName("agentMode")] + public SendAgentMode AgentMode { get; set; } - /// Identifier for this entry, formed by joining its `kind` and source name (e.g. `tool:bash`, `skill:tmux`, `toolDefinition:bash`); unique within the snapshot. Use it to match the same entry across snapshots, to correlate with other APIs (skill/agent/MCP registries), and as the `parentId` target for nesting. Distinct from the human-facing `label`. + /// Human-readable text to display for this queue entry in the UI. + [JsonPropertyName("displayText")] + public string DisplayText { get; set; } = string.Empty; + + /// Stable opaque id for the canonical queued item. Batch rows share one id. [JsonPropertyName("id")] public string Id { get; set; } = string.Empty; - /// Source category for this entry. Not a closed set — tolerate unknown values. Known values today: `skill`, `subagent`, `mcpServer`, `tool`, `system`, `toolDefinition`, `plugin`. + /// Whether this item is a queued user message or a queued slash command / model change. [JsonPropertyName("kind")] - public string Kind { get; set; } = string.Empty; - - /// Human-readable display label, e.g. `bash` or `skill: tmux`. Presentation-only; may be localized/reformatted without notice — do not key off it. - [JsonPropertyName("label")] - public string Label { get; set; } = string.Empty; + public QueuePendingItemsKind Kind { get; set; } - /// Optional `id` of the parent entry: e.g. a `plugin` entry parenting its `skill`/`mcpServer` entries, or the `system` entry parenting `toolDefinition` entries. Omitted for top-level entries. - [JsonPropertyName("parentId")] - public string? ParentId { get; set; } + /// Stable identity of the queued user message. Present for message rows and absent for slash commands and model changes. + [JsonPropertyName("messageId")] + public string? MessageId { get; set; } - /// Token count currently in context attributable to this entry. - [JsonPropertyName("tokens")] - public long Tokens { get; set; } + /// Optional source tag associated with this pending queue entry. This is an open string, not authenticated authorship. In particular, `user` does not prove that a person typed the message. If the source is absent or unrecognized, consumers must not infer human or agent authorship and should handle the entry neutrally. Consumers should tolerate future source values. + [JsonPropertyName("source")] + public string? Source { get; set; } } -/// Per-source token attribution snapshot for the current context window. The heaviest individual messages are available separately via `metadata.getContextHeaviestMessages`. -public sealed class MetadataContextAttributionResultContextAttribution +/// Snapshot of the session's pending queued items and immediate-steering messages. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class QueuePendingItemsResult { - /// Output reservation overlapping the displayed prompt allowance plus the tokens past the effective input budget's buffer-exhaustion blocking threshold. Mirrors `SessionContextInfo.bufferTokens`. - [JsonPropertyName("bufferTokens")] - public long BufferTokens { get; set; } - - /// The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. - [JsonPropertyName("categories")] - public MetadataContextAttributionResultContextAttributionCategories Categories { get => field ??= new(); set; } + /// How many leading entries of `steeringMessages` have already been folded into the running turn (and so have an emitted `user.message`), as opposed to still waiting for one. Absent for hosts that do not distinguish the two. + [JsonPropertyName("inFlightSteeringCount")] + public long? InFlightSteeringCount { get; set; } - /// Successful compaction history for the session. - [JsonPropertyName("compactions")] - public MetadataContextAttributionResultContextAttributionCompactions Compactions { get => field ??= new(); set; } + /// Pending queued items in submission order. Includes user messages, queued slash commands, and queued model changes; omits internal system items. + [JsonPropertyName("items")] + public IList Items { get => field ??= []; set; } - /// Token count at which background compaction starts. Mirrors `SessionContextInfo.compactionThreshold`. - [JsonPropertyName("compactionThreshold")] - public long CompactionThreshold { get; set; } + /// Display text for messages currently in the immediate steering queue (interjections sent during a running turn). + [JsonPropertyName("steeringMessages")] + public IList SteeringMessages { get => field ??= []; set; } - /// Flat list of per-source attribution entries. Group by `kind` and render unrecognized kinds generically. Nesting and rollups are expressed via `parentId`. - [JsonPropertyName("entries")] - public IList Entries { get => field ??= []; set; } + /// ID of the running turn's user message while the model has not answered it, so `withdrawMessage` can still take it back once nothing sent after it is pending. A message leaves `items` when its turn starts, before its `user.message` is recorded; this tells that message apart from one that was removed. Absent when no turn prompt can be taken back. + [JsonPropertyName("withdrawableTurnMessageId")] + public string? WithdrawableTurnMessageId { get; set; } +} - /// Advertised prompt allowance for the selected context tier: the denominator for context-usage displays and capacity for `categories.freeSpace` and `categories.buffer`. Mirrors `SessionContextInfo.limit`. - [JsonPropertyName("limit")] - public long Limit { get; set; } +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionQueuePendingItemsRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// The concrete model id the entire breakdown was tokenized against (feeds the per-model token multiplier). Under `Auto` (Free/Student) this is the resolved model, not the literal `auto` sentinel, so totals are not undercounted. A single-model approximation of a potentially multi-model Auto session. - [JsonPropertyName("modelId")] - public string ModelId { get; set; } = string.Empty; +/// Internal snapshot of native queue state for local session orchestration. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class QueueSnapshotResult +{ + /// Queue item identifier of a model switch that has been dequeued but not yet applied. + [JsonPropertyName("inFlightModelChangeId")] + public string? InFlightModelChangeId { get; set; } - /// How `modelId` was chosen. Not a closed set — tolerate unknown values. Known values today: `autoResolved` (the model Auto resolved to), `selected` (the user's explicitly selected model), `default` (a fallback before any model is known). - [JsonPropertyName("modelSource")] - public string ModelSource { get; set; } = string.Empty; + /// Insertion orders for queued items, aligned with `items`. + [JsonPropertyName("itemOrders")] + public IList? ItemOrders { get; set; } - /// Effective input budget after reserving requested output against the combined context ceiling. Mirrors `SessionContextInfo.promptTokenLimit`. - [JsonPropertyName("promptTokenLimit")] - public long PromptTokenLimit { get; set; } + /// User-facing pending items in FIFO order. + [JsonPropertyName("items")] + public IList Items { get => field ??= []; set; } - /// Total token count of the current context window the entries are measured against (system message + conversation messages + tool definitions — the same total reported by /context). Divide an entry's `tokens` by this to derive its share. - [JsonPropertyName("totalTokens")] - public long TotalTokens { get; set; } -} + /// Insertion orders for immediate steering messages, aligned with `steeringMessages`. + [JsonPropertyName("steeringMessageOrders")] + public IList? SteeringMessageOrders { get; set; } -/// Per-source attribution breakdown for the session's current context window, or null if uninitialized. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataContextAttributionResult -{ - /// Per-source context-window attribution, or null if the session has not yet been initialized (no system prompt or tool metadata cached). - [JsonPropertyName("contextAttribution")] - public MetadataContextAttributionResultContextAttribution? ContextAttribution { get; set; } + /// Immediate steering messages waiting for an active turn. + [JsonPropertyName("steeringMessages")] + public IList SteeringMessages { get => field ??= []; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMetadataGetContextAttributionRequest +internal sealed class SessionQueueSnapshotRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// A single large message currently in context. +/// Result of moving a queued item. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ContextHeaviestMessage +public sealed class QueueMoveItemResult { - /// Stable identifier for this message within the snapshot. + /// True when the item changed position; false when it was already at the requested position. + [JsonPropertyName("changed")] + public bool Changed { get; set; } +} + +/// Parameters for moving a queued item by stable id. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class QueueMoveItemRequest +{ + /// Stable opaque queued-item id. [JsonPropertyName("id")] public string Id { get; set; } = string.Empty; - /// Human-readable source label, e.g. `tool: bash` or `skill: tmux`. Presentation-only. - [JsonPropertyName("label")] - public string Label { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Role of the chat message (`user`, `assistant`, or `tool`). - [JsonPropertyName("role")] - public string Role { get; set; } = string.Empty; + /// Zero-based target position in the public visible queue. Values outside the queue clamp to an end. + [JsonPropertyName("toPosition")] + public long ToPosition { get; set; } +} - /// Token count currently in context for this individual message. - [JsonPropertyName("tokens")] - public long Tokens { get; set; } +/// Result of inserting a queued message. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class QueueInsertAtResult +{ + /// Fresh stable opaque id assigned to the inserted item. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; } -/// The heaviest individual messages in the session's context window, most-expensive first. +/// Serializable message fields accepted by queue.insertAt. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataContextHeaviestMessagesResult +public sealed class QueueInsertMessage { - /// Heaviest messages, most-expensive first. - [JsonPropertyName("messages")] - public IList Messages { get => field ??= []; set; } + /// Optional explicit agent mode. When omitted, the session's current mode is assigned. + [JsonPropertyName("agentMode")] + public SendAgentMode? AgentMode { get; set; } - /// Total token count of the current context window, so callers can compute each message's share without a second call. - [JsonPropertyName("totalTokens")] - public long TotalTokens { get; set; } + /// Optional attachments for the message. + [JsonPropertyName("attachments")] + public IList? Attachments { get; set; } + + /// Whether the message is billable. + [JsonPropertyName("billable")] + public bool? Billable { get; set; } + + /// Accepted for internal SendOptions compatibility but ignored; delivery is derived from current session activity. + [JsonPropertyName("delivery")] + public string? Delivery { get; set; } + + /// Optional user-facing display text. + [JsonPropertyName("displayPrompt")] + public string? DisplayPrompt { get; set; } + + /// Accepted for SendOptions compatibility but ignored; inserted items always use queued delivery semantics. + [JsonPropertyName("mode")] + public SendMode? Mode { get; set; } + + /// Accepted for SendOptions compatibility but ignored; the requested public position controls placement. + [JsonPropertyName("prepend")] + public bool? Prepend { get; set; } + + /// The user message text. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; + + /// Per-turn request headers. + [JsonPropertyName("requestHeaders")] + public IDictionary? RequestHeaders { get; set; } + + /// Required tool name for the turn, when any. + [JsonPropertyName("requiredTool")] + public string? RequiredTool { get; set; } + + /// Optional provenance source. `system` is rejected: it would hide the inserted row from `pendingItems` and make it unaddressable while still executing, so inserted items must stay visible. + [JsonPropertyName("source")] + public string? Source { get; set; } + + /// Accepted for SendOptions compatibility but ignored; insertion scheduling is controlled by the queue drain state. + [JsonPropertyName("wait")] + public bool? Wait { get; set; } } -/// Parameters for the heaviest-messages query. +/// Parameters for inserting a queued message at a public visible position. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class MetadataContextHeaviestMessagesRequest +internal sealed class QueueInsertAtRequest { - /// Maximum number of messages to return, most-expensive first. Omit for the server default. - [JsonPropertyName("limit")] - public long? Limit { get; set; } + /// Queued message contents and delivery metadata. + [JsonPropertyName("message")] + public QueueInsertMessage Message { get => field ??= new(); set; } + + /// Zero-based position in the public visible queue. Values outside the queue clamp to an end. + [JsonPropertyName("position")] + public long Position { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// 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). For a local session, a report whose `cwd` diverges from the session's current working directory is ignored (the call still succeeds but records nothing and emits no event); move a local session's working directory via `metadata.setWorkingDirectory` instead. +/// Result of removing a queued item. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataRecordContextChangeResult +public sealed class QueueRemoveAtResult { + /// True when the addressed item was removed. + [JsonPropertyName("removed")] + public bool Removed { get; set; } } -/// Updated working-directory/git context to record on the session. +/// Parameters for removing a queued item by stable id. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class MetadataRecordContextChangeRequest +internal sealed class QueueRemoveAtRequest { - /// Updated working directory and git context. Emitted as the new payload of `session.context_changed`. - [JsonPropertyName("context")] - public SessionWorkingDirectoryContext Context { get => field ??= new(); set; } + /// Stable opaque ID of the queued item to remove. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// 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 any related side-effects (file index, etc.); it does NOT change the process working directory (a session's cwd is per-session, not process-global). For local sessions the runtime validates the target first (an absolute path that exists on disk) and re-bases the permission primary directory; a rejected validation fails the call before anything is mutated, persisted, or emitted. Location-scoped permission rules are then re-keyed to the new directory (best-effort). Remote sessions only record the path. +/// Result of editing a queued message. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataSetWorkingDirectoryResult +public sealed class QueueUpdateTextResult { - /// Working directory after the update. - [JsonPropertyName("workingDirectory")] - public string WorkingDirectory { get; set; } = string.Empty; + /// True when the stored text changed. + [JsonPropertyName("updated")] + public bool Updated { get; set; } } -/// Absolute path to set as the session's new working directory. For local sessions the path must be absolute and exist on disk: it is validated before any session state changes, and a failing validation rejects the call with nothing mutated, persisted, or emitted. Remote sessions record the path as-is. +/// Parameters for editing a single queued message. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class MetadataSetWorkingDirectoryRequest +internal sealed class QueueUpdateTextRequest { + /// Optional replacement prompt displayed to the user. + [JsonPropertyName("displayPrompt")] + public string? DisplayPrompt { get; set; } + + /// Stable opaque ID of the queued item to edit. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Replacement prompt sent to the model. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// Absolute path to set as the session's working directory. The runtime updates the session's recorded cwd so subsequent operations (shell tools, file lookups, telemetry) anchor to it. - [JsonPropertyName("workingDirectory")] - public string WorkingDirectory { get; set; } = string.Empty; } -/// 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. +/// Result of withdrawing a user message. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataRecomputeContextTokensResult +public sealed class QueueWithdrawMessageResult { - /// Tokens contributed by user/assistant/tool messages (excludes system/developer prompts). - [JsonPropertyName("messagesTokenCount")] - public long MessagesTokenCount { get; set; } - - /// Tokens contributed by system/developer prompt snapshots. - [JsonPropertyName("systemTokenCount")] - public long SystemTokenCount { get; set; } + /// True when the running turn was interrupted to withdraw the message. With removed false, the turn was interrupted but its events could not be removed, for example because the model answered first, so the message stays in the interrupted turn. + [JsonPropertyName("interrupted")] + public bool Interrupted { get; set; } - /// Sum of tokens across chat-context and system-context messages currently held by the session. - [JsonPropertyName("totalTokens")] - public long TotalTokens { get; set; } + /// True when the message left the queue or, for a running turn, history. + [JsonPropertyName("removed")] + public bool Removed { get; set; } } -/// Model identifier to use when re-tokenizing the session's existing messages. +/// Conditional withdrawal of a single user message, from its queue or from the running turn it started. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class MetadataRecomputeContextTokensRequest +internal sealed class QueueWithdrawMessageRequest { - /// Model identifier used for tokenization. The runtime token-counts both chat-context and system-context messages against this model. - [JsonPropertyName("modelId")] - public string ModelId { get; set; } = string.Empty; + /// The prompt originally sent. A message edited since submission is not withdrawn, so an obsolete draft cannot replace the edit. + [JsonPropertyName("expectedPrompt")] + public string ExpectedPrompt { get; set; } = string.Empty; + + /// Message identity returned by send, not the queue item id. Batch messages are not eligible. + [JsonPropertyName("messageId")] + public string MessageId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Availability of built-in job tools surfaced to boundary consumers. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionSettingsBuiltInToolAvailabilitySnapshot -{ - /// Whether the create-pull-request tool is available. - [JsonPropertyName("createPullRequest")] - public bool? CreatePullRequest { get; set; } - - /// Whether the report-progress tool is available. - [JsonPropertyName("reportProgress")] - public bool? ReportProgress { get; set; } -} - -/// Redacted job settings for a session. The job nonce is excluded. +/// Append to one pending steering message without changing its identity or delivery position. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionSettingsJobSnapshot +internal sealed class QueueAppendSteeringRequest { - /// Availability of job-specific built-in tools. - [JsonPropertyName("builtInToolAvailability")] - public SessionSettingsBuiltInToolAvailabilitySnapshot? BuiltInToolAvailability { get; set; } + /// Mode captured at submission. Only steering messages in the same mode may be combined. + [JsonPropertyName("agentMode")] + public SendAgentMode AgentMode { get; set; } - /// GitHub Actions event type for the job. - [JsonPropertyName("eventType")] - public string? EventType { get; set; } + /// Attachments to add after the message's existing attachments. An empty list preserves the existing attachments. + [JsonPropertyName("attachments")] + public IList Attachments { get => field ??= []; set; } - /// Whether this is the workflow's trigger job. - [JsonPropertyName("isTriggerJob")] - public bool? IsTriggerJob { get; set; } -} + /// Display text to append to the existing preview after a blank line. + [JsonPropertyName("displayPrompt")] + public string DisplayPrompt { get; set; } = string.Empty; -/// Redacted model routing settings for a session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionSettingsModelSnapshot -{ - /// Agent service callback URL for job and progress updates. - [JsonPropertyName("callbackUrl")] - public string? CallbackUrl { get; set; } + /// Expected current prompt, including any previous appends. The runtime applies plan-mode normalization before comparing and refuses a changed message. + [JsonPropertyName("expectedPrompt")] + public string ExpectedPrompt { get; set; } = string.Empty; - /// Default reasoning effort for the selected model. - [JsonPropertyName("defaultReasoningEffort")] - public string? DefaultReasoningEffort { get; set; } + /// Message identity returned by send, not the queue item id. Only unclaimed user steering messages are eligible. + [JsonPropertyName("messageId")] + public string MessageId { get; set; } = string.Empty; - /// Agent job identifier for the session. - [JsonPropertyName("instanceId")] - public string? InstanceId { get; set; } + /// Text to append after a blank line. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; - /// Selected model identifier. - [JsonPropertyName("model")] - public string? Model { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Online-evaluation settings safe to expose across the SDK boundary. +/// Result of duplicating a queued item. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionSettingsOnlineEvaluationSnapshot +public sealed class QueueDuplicateAtResult { - /// Whether online evaluation is disabled. - [JsonPropertyName("disableOnlineEvaluation")] - public bool? DisableOnlineEvaluation { get; set; } - - /// Whether online-evaluation output-file generation is enabled. - [JsonPropertyName("enableOnlineEvaluationOutputFile")] - public bool? EnableOnlineEvaluationOutputFile { get; set; } + /// Fresh stable opaque id assigned to the duplicate. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; } -/// Redacted repository and GitHub host settings for a session. +/// Parameters for duplicating a queued item. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionSettingsRepoSnapshot +internal sealed class QueueDuplicateAtRequest { - /// Checked-out repository branch. - [JsonPropertyName("branch")] - public string? Branch { get; set; } - - /// Checked-out commit SHA. - [JsonPropertyName("commit")] - public string? Commit { get; set; } - - /// GitHub server host name. - [JsonPropertyName("host")] - public string? Host { get; set; } - - /// Protocol used to access the GitHub host. - [JsonPropertyName("hostProtocol")] - public string? HostProtocol { get; set; } - - /// GitHub repository database ID. + /// Stable opaque ID of the queued item to duplicate. [JsonPropertyName("id")] - public double? Id { get; set; } - - /// Repository name. - [JsonPropertyName("name")] - public string? Name { get; set; } - - /// GitHub repository owner database ID. - [JsonPropertyName("ownerId")] - public double? OwnerId { get; set; } - - /// Repository owner login. - [JsonPropertyName("ownerName")] - public string? OwnerName { get; set; } - - /// Number of commits in the pull request. - [JsonPropertyName("prCommitCount")] - public double? PrCommitCount { get; set; } + public string Id { get; set; } = string.Empty; - /// Whether the repository is writable. - [JsonPropertyName("readWrite")] - public bool? ReadWrite { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// GitHub secret-scanning service URL. - [JsonPropertyName("secretScanningUrl")] - public string? SecretScanningUrl { get; set; } +/// Parameters for acquiring or releasing the queued-lane drain pause. Acquisition is exclusive and non-idempotent: `paused: true` against an already-paused session fails with `queue_already_paused`. The pause is never released automatically — it is not tied to the caller's lifetime, so a client that exits without sending `paused: false` leaves the lane frozen. Release is unowned: `paused: false` clears the pause for any caller, including one that never acquired it. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class QueueSetDrainPausedRequest +{ + /// Whether queued-lane draining should be paused. + [JsonPropertyName("paused")] + public bool Paused { get; set; } - /// GitHub server base URL. - [JsonPropertyName("serverUrl")] - public string? ServerUrl { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Redacted validation and memory-tool settings for a session. +/// Result of trying to steer a queued message into a live turn. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionSettingsValidationSnapshot +public sealed class QueueSendNowResult { - /// Whether advisory validation is enabled. - [JsonPropertyName("advisoryEnabled")] - public bool? AdvisoryEnabled { get; set; } - - /// Whether CodeQL validation is enabled. - [JsonPropertyName("codeqlEnabled")] - public bool? CodeqlEnabled { get; set; } - - /// Whether code-review validation is enabled. - [JsonPropertyName("codeReviewEnabled")] - public bool? CodeReviewEnabled { get; set; } - - /// Model used for code-review validation. - [JsonPropertyName("codeReviewModel")] - public string? CodeReviewModel { get; set; } - - /// Dependabot validation timeout budget in seconds. - [JsonPropertyName("dependabotTimeout")] - public double? DependabotTimeout { get; set; } - - /// Whether the memory-store tool is enabled. - [JsonPropertyName("memoryStoreEnabled")] - public bool? MemoryStoreEnabled { get; set; } + /// True when the item was accepted into the steering lane; false when no main turn was live. + [JsonPropertyName("steered")] + public bool Steered { get; set; } +} - /// Whether the memory-vote tool is enabled. - [JsonPropertyName("memoryVoteEnabled")] - public bool? MemoryVoteEnabled { get; set; } +/// Parameters for steering a queued message into a live turn. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class QueueSendNowRequest +{ + /// Stable opaque ID of the queued item to steer into the live turn. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// Whether secret-scanning validation is enabled. - [JsonPropertyName("secretScanningEnabled")] - public bool? SecretScanningEnabled { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// General validation timeout budget in seconds. - [JsonPropertyName("timeout")] - public double? Timeout { get; set; } +/// Whether the native queue has pending work. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class QueueHasPendingResult +{ + /// True when queued or immediate native work is pending. + [JsonPropertyName("hasPending")] + public bool HasPending { get; set; } } -/// Redacted, serializable view of session runtime settings for SDK boundary consumers. Secrets and raw feature flags are intentionally excluded. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSettingsSnapshot +internal sealed class SessionQueueHasPendingRequest { - /// Name of the SDK client that created the session. - [JsonPropertyName("clientName")] - public string? ClientName { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Redacted job settings. - [JsonPropertyName("job")] - public SessionSettingsJobSnapshot Job { get => field ??= new(); set; } +/// Whether a deferred-idle drain should run. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class QueueBeginDeferredIdleDrainResult +{ + /// True when the host should run finishDeferredIdleDrain asynchronously. + [JsonPropertyName("shouldDrain")] + public bool ShouldDrain { get; set; } +} - /// Redacted model routing settings. - [JsonPropertyName("model")] - public SessionSettingsModelSnapshot Model { get => field ??= new(); set; } +/// Inputs for starting a deferred-idle drain. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class QueueBeginDeferredIdleDrainRequest +{ + /// Whether the host still has active background work. + [JsonPropertyName("activeBackgroundWork")] + public bool ActiveBackgroundWork { get; set; } - /// Online-evaluation settings safe for SDK consumers. - [JsonPropertyName("onlineEvaluation")] - public SessionSettingsOnlineEvaluationSnapshot OnlineEvaluation { get => field ??= new(); set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Redacted repository and host settings. - [JsonPropertyName("repo")] - public SessionSettingsRepoSnapshot Repo { get => field ??= new(); set; } +/// Action selected by the native deferred-idle drain. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class QueueFinishDeferredIdleDrainResult +{ + /// Whether the deferred idle was caused by an aborted foreground turn. + [JsonPropertyName("aborted")] + public bool Aborted { get; set; } - /// Session start time as Unix epoch milliseconds. - [JsonPropertyName("startTimeMs")] - public double? StartTimeMs { get; set; } + /// One of none, processQueue, or emitSessionIdle. + [JsonPropertyName("action")] + public string Action { get; set; } = string.Empty; +} - /// Session timeout in milliseconds. - [JsonPropertyName("timeoutMs")] - public double? TimeoutMs { get; set; } +/// Inputs for completing a deferred-idle drain. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class QueueFinishDeferredIdleDrainRequest +{ + /// Whether the host still has active background work. + [JsonPropertyName("activeBackgroundWork")] + public bool ActiveBackgroundWork { get; set; } - /// Redacted validation and memory-tool settings. - [JsonPropertyName("validation")] - public SessionSettingsValidationSnapshot Validation { get => field ??= new(); set; } + /// Whether native queued work remains. + [JsonPropertyName("hasPending")] + public bool HasPending { get; set; } - /// Agent runtime version selector copied from the session settings, such as `latest` or a runtime release identifier. - [JsonPropertyName("version")] - public string? Version { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Identifies the target session. +/// Inputs for marking session.idle deferred in native state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSettingsSnapshotRequest +internal sealed class QueueDeferSessionIdleRequest { + /// Whether the deferred idle was caused by an aborted foreground turn. + [JsonPropertyName("aborted")] + public bool Aborted { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of evaluating a Rust-owned settings predicate. +/// Indicates whether a user-facing pending item was removed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSettingsEvaluatePredicateResult +public sealed class QueueRemoveMostRecentResult { - /// Whether the named settings predicate evaluated to enabled. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } + /// True if a user-facing pending item was removed (LIFO across both queues); false when no removable items remained. + [JsonPropertyName("removed")] + public bool Removed { get; set; } } -/// Named Rust-owned settings predicate to evaluate for this session. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSettingsEvaluatePredicateRequest +internal sealed class SessionQueueRemoveMostRecentRequest { - /// Predicate name. The runtime owns the raw feature-flag names and composition logic. - [JsonPropertyName("name")] - public SessionSettingsPredicateName Name { get; set; } - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// Tool name for tool-scoped predicates such as trivial-change handling. - [JsonPropertyName("toolName")] - public string? ToolName { get; set; } } -/// Content-exclusion decision for one requested path. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ContentExclusionPathCheck +internal sealed class SessionQueueClearRequest { - /// Whether the session's complete content-exclusion policy excludes the path. - [JsonPropertyName("excluded")] - public bool Excluded { get; set; } - - /// The path supplied by the caller. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Batch content-exclusion result. Callers must fail closed when policy evaluation is unavailable. +/// Internal filter for consuming queued system notifications. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ContentExclusionCheckPathsResult +internal sealed class QueueConsumeSystemNotificationsRequest { - /// Whether the session's policy service was available for the complete batch. When false, checks is empty and callers must treat every requested path as excluded. - [JsonPropertyName("available")] - public bool Available { get; set; } + /// Opaque runtime-owned filter object. + [JsonPropertyName("filter")] + public JsonElement Filter { get; set; } - /// Per-path decisions in request order. Empty when available is false. - [JsonPropertyName("checks")] - public IList Checks { get => field ??= []; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Local file system absolute paths within the session working directory to check against its content-exclusion policy. +/// Result of enqueueing the resume-pending wake item. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ContentExclusionCheckPathsRequest +internal sealed class QueueEnqueueResumePendingResult { - /// Local file system absolute paths within the session working directory to check. Results are returned in the same order, including duplicates. - [JsonPropertyName("paths")] - public IList Paths { get => field ??= []; set; } + /// True when a wake item was newly queued. + [JsonPropertyName("queued")] + public bool Queued { get; set; } +} +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionQueueEnqueueResumePendingRequest +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Identifier of the spawned shell process, usable with shell.kill while the process is running. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ShellExecResult +internal sealed class SessionQueueProcessRequest { - /// Identifier usable with shell.kill while the process is running. - [JsonPropertyName("processId")] - public string ProcessId { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Shell command to run, with optional working directory and timeout in milliseconds. Spawn failures return an RPC error. +/// Cursor, batch size, and optional long-poll/filter parameters for reading session events. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ShellExecRequest +internal sealed class EventLogReadRequest { - /// Shell command to execute. - [JsonPropertyName("command")] - public string Command { get; set; } = string.Empty; + /// Optional non-empty list of subagent identifiers. When provided, only events owned by one of these agents are returned; ownership recognizes the event envelope's agentId plus legacy data.agentId and data.parentToolCallId markers. This filter takes precedence over agentScope. + [JsonPropertyName("agentIds")] + public IList? AgentIds { get; set; } - /// Working directory (defaults to session working directory). - [JsonPropertyName("cwd")] - public string? Cwd { get; set; } + /// Agent-scope filter: 'primary' returns only main-agent events plus events whose type starts with 'subagent.' (matching the typed-subscription default behavior); 'all' returns events from all agents (matching wildcard-subscription behavior). Default is 'all' to preserve wildcard semantics for catch-up callers. + [JsonPropertyName("agentScope")] + public EventsAgentScope? AgentScope { get; set; } + + /// Opaque cursor returned by a previous read. Omit on the first call to start from the beginning of the session's persisted history. + [JsonPropertyName("cursor")] + public string? Cursor { get; set; } + + /// Direction to page through the session's persisted event history. 'forward' (default) pages from the cursor toward newer events (or from the start of history when no cursor is given). 'backward' enables tail-first reads: with no cursor it returns the NEWEST `max` events, and the returned cursor pages toward OLDER events on subsequent backward reads. Events within a returned batch are always in chronological (oldest-to-newest) order, even for a backward read. Backward reads cover PERSISTED history only; ephemeral events are never returned by a backward read. `direction` selects the INITIAL read only: the returned cursor is self-describing, so a continuation read pages in the cursor's own direction regardless of the `direction` passed alongside it — a forward cursor always pages forward and a backward cursor always pages backward. Pass the direction that matches the cursor to avoid confusion. + [JsonPropertyName("direction")] + public EventsReadDirection? Direction { get; set; } + + /// When false, skip ephemeral events entirely and return only durable (persisted) events. History-backfill callers that discard ephemerals anyway should set this so the read is bounded by the durable log length instead of racing the ephemeral ring on a busy session. Defaults to true (ephemerals are interleaved with durable events in creation order). Ignored by backward reads, which always cover persisted history only. + [JsonPropertyName("includeEphemeral")] + public bool? IncludeEphemeral { get; set; } + + /// Maximum number of events to return in this batch (1–1000, default 200). + [JsonPropertyName("max")] + public long? Max { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - /// Timeout in milliseconds (default: 30000). + /// Either '*' to receive all event types, or a non-empty list of event types to receive. + [JsonPropertyName("types")] + public JsonElement? Types { get; set; } + + /// Milliseconds to wait for new events when the cursor is at the tail of history. 0 (default) returns immediately even if no events are available. Capped at 30000ms. Ephemeral events that arrive during the wait are delivered in this batch but are NOT replayable on a subsequent read (use a non-zero waitMs in your next call to capture future ephemerals as they happen). This applies to forward reads only: a backward read always returns immediately and ignores `waitMs`, because backward paging covers persisted history only while new events append at the tail (the opposite end from a backward page), so no blocking or ephemeral delivery can occur. [JsonConverter(typeof(MillisecondsTimeSpanConverter))] - [JsonPropertyName("timeout")] - public TimeSpan? Timeout { get; set; } + [JsonPropertyName("waitMs")] + public TimeSpan? Wait { get; set; } } -/// Indicates whether the signal was delivered; false if the process was unknown or already exited. +/// 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). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ShellKillResult +public sealed class EventLogTailResult { - /// Whether the signal was sent successfully. - [JsonPropertyName("killed")] - public bool Killed { get; set; } + /// Opaque cursor pointing at the current tail of the session's persisted-events history. Pass back to `read` to receive only events that arrive AFTER this snapshot. When the session has no events, this returns the same sentinel as an unset cursor (i.e. equivalent to omitting the cursor on a first read). + [JsonPropertyName("cursor")] + public string Cursor { get; set; } = string.Empty; } -/// Identifier of a process previously returned by "shell.exec" and the signal to send. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ShellKillRequest +internal sealed class SessionEventLogTailRequest { - /// Process identifier returned by shell.exec. - [JsonPropertyName("processId")] - public string ProcessId { get; set; } = string.Empty; - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// Signal to send (default: SIGTERM). - [JsonPropertyName("signal")] - public ShellKillSignal? Signal { get; set; } } -/// Result of a user-requested shell command. +/// Opaque handle representing an event-type interest registration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UserRequestedShellCommandResult +public sealed class RegisterEventInterestResult { - /// Error output when the execution failed. - [JsonPropertyName("error")] - public string? Error { get; set; } + /// Opaque handle for this registration. Pass to releaseInterest to release. Each call to registerInterest produces a fresh handle, even when the same eventType is registered multiple times. + [JsonPropertyName("handle")] + public string Handle { get; set; } = string.Empty; +} - /// Process exit code, when available. - [JsonPropertyName("exitCode")] - public long? ExitCode { get; set; } +/// Event type to register consumer interest for, used by runtime gating logic. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class RegisterEventInterestParams +{ + /// The event type the consumer wants the runtime to treat as 'observed' for behavior-switching gating. Some runtime code paths inspect whether any consumer is interested in a specific event type and choose a different implementation accordingly (e.g. `mcp.oauth_required`: when interest is registered the runtime delegates interactive OAuth token acquisition to the consumer via `mcp.oauth_required` events; when no interest is registered the runtime still attempts non-interactive reconnect from cached or refreshable tokens, and only marks the server `needs-auth` if usable credentials are unavailable — it does not open a browser or start interactive OAuth without a consumer). SDK clients that long-poll events do NOT automatically appear as listeners to these gating checks — they must explicitly call `registerInterest` for each event type they want the runtime to count as having a consumer. Multiple registrations for the same event type from the same or different consumers are tracked independently and must each be released. See: `mcp.oauth_required`, `sampling.requested`, `auto_mode_switch.requested`, `session_limits_exhausted.requested`, `user_input.requested`, `elicitation.requested`, `command.queued`, `exit_plan_mode.requested`. + [JsonPropertyName("eventType")] + public string EventType { get; set; } = string.Empty; - /// Captured command output. - [JsonPropertyName("output")] - public string Output { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Whether the command completed successfully. +/// Indicates whether the operation succeeded. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class EventLogReleaseInterestResult +{ + /// Whether the operation succeeded. [JsonPropertyName("success")] public bool Success { get; set; } - - /// Tool call id emitted for the shell execution. - [JsonPropertyName("toolCallId")] - public string ToolCallId { get; set; } = string.Empty; } -/// User-requested shell command and cancellation handle. +/// Opaque handle previously returned by `registerInterest` to release. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ShellExecuteUserRequestedRequest +internal sealed class ReleaseEventInterestParams { - /// Shell command to execute. - [JsonPropertyName("command")] - public string Command { get; set; } = string.Empty; - - /// Caller-provided cancellation handle for this execution. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// Handle returned by a previous `registerInterest` call. Idempotent: releasing an unknown or already-released handle is a no-op (returns success). When the last outstanding handle for an event type is released, the runtime reverts to its 'no consumer' code path for that event type. + [JsonPropertyName("handle")] + public string Handle { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Cancellation result for a user-requested shell command. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CancelUserRequestedShellCommandResult +internal sealed class SessionUsageGetMetricsRequest { - /// Whether an in-flight execution was found and signalled to cancel. - [JsonPropertyName("cancelled")] - public bool Cancelled { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// User-requested shell execution cancellation handle. +/// Internal absolute code-change totals reported by the owning host. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ShellCancelUserRequestedRequest +internal sealed class SessionUsageSetCodeChangesRequest { - /// Request ID previously passed to executeUserRequested. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// Absolute changed-file count; omission preserves the previous count. + [JsonPropertyName("filesCount")] + public double? FilesCount { get; set; } + + /// Absolute added-line total, replacing the previous reading. + [JsonPropertyName("linesAdded")] + public double LinesAdded { get; set; } + + /// Absolute removed-line total, replacing the previous reading. + [JsonPropertyName("linesRemoved")] + public double LinesRemoved { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Post-compaction context window usage breakdown. +/// Existing delegation guidance for an account without delegation access. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistoryCompactContextWindow +public sealed class SessionQuotaDelegateWarning { - /// Token count from non-system messages (user, assistant, tool). - [JsonPropertyName("conversationTokens")] - public long? ConversationTokens { get; set; } + /// User-facing guidance. + [JsonPropertyName("text")] + public string Text { get; set; } = string.Empty; - /// Current total tokens in the context window (system + conversation + tool definitions). - [JsonPropertyName("currentTokens")] - public long CurrentTokens { get; set; } + /// Link to upgrade the account. + [JsonPropertyName("url")] + public string Url { get; set; } = string.Empty; +} - /// Current number of messages in the conversation. - [JsonPropertyName("messagesLength")] - public long MessagesLength { get; set; } +/// Session quota snapshot, preserving the incumbent numeric units and reset metadata. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionQuotaSnapshot +{ + /// Entitled quantity in this quota's units. + [JsonPropertyName("entitlementRequests")] + public double EntitlementRequests { get; set; } - /// Token count from system message(s). - [JsonPropertyName("systemTokens")] - public long? SystemTokens { get; set; } + /// Whether the provider reports available quota. + [JsonPropertyName("hasQuota")] + public bool? HasQuota { get; set; } - /// Maximum token count for the model's context window. - [JsonPropertyName("tokenLimit")] - public long TokenLimit { get; set; } + /// Whether the entitlement is unlimited. + [JsonPropertyName("isUnlimitedEntitlement")] + public bool IsUnlimitedEntitlement { get; set; } - /// Token count from tool definitions. - [JsonPropertyName("toolDefinitionsTokens")] - public long? ToolDefinitionsTokens { get; set; } -} + /// Additional usage in this quota's units. + [JsonPropertyName("overage")] + public double Overage { get; set; } -/// Compaction outcome with the number of tokens and messages removed, summary text, and the resulting context window breakdown. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistoryCompactResult -{ - /// Post-compaction context window usage breakdown. - [JsonPropertyName("contextWindow")] - public HistoryCompactContextWindow? ContextWindow { get; set; } + /// Whether additional usage is allowed after quota exhaustion. + [JsonPropertyName("overageAllowedWithExhaustedQuota")] + public bool OverageAllowedWithExhaustedQuota { get; set; } - /// Number of messages removed during compaction. - [JsonPropertyName("messagesRemoved")] - public long MessagesRemoved { get; set; } + /// Additional-usage budget cap, when provided. + [JsonPropertyName("overageEntitlement")] + public double? OverageEntitlement { get; set; } - /// Whether compaction completed successfully. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Percentage of the entitlement remaining. + [JsonPropertyName("remainingPercentage")] + public double RemainingPercentage { get; set; } - /// Summary text produced by compaction. Omitted when compaction did not produce a summary (e.g. failure path). - [JsonPropertyName("summaryContent")] - public string? SummaryContent { get; set; } + /// Quota reset time in milliseconds since the Unix epoch, when known. + [JsonPropertyName("resetDateEpochMs")] + public double? ResetDateEpochMs { get; set; } - /// Number of tokens freed by compaction. - [JsonPropertyName("tokensRemoved")] - public long TokensRemoved { get; set; } + /// Whether the reset time is estimated. + [JsonPropertyName("resetDateEstimated")] + public bool? ResetDateEstimated { get; set; } + + /// Whether this quota uses token-based billing. + [JsonPropertyName("tokenBasedBilling")] + public bool? TokenBasedBilling { get; set; } + + /// Whether usage is allowed after quota exhaustion. + [JsonPropertyName("usageAllowedWithExhaustedQuota")] + public bool UsageAllowedWithExhaustedQuota { get; set; } + + /// Consumed quantity in this quota's units. + [JsonPropertyName("usedRequests")] + public double UsedRequests { get; set; } } -/// RPC data type for SessionHistoryCompact operations. +/// The incumbent session-owned quota and account projection. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionHistoryCompactRequest +public sealed class SessionQuotaGetResult { - /// Optional user-provided instructions to focus the compaction summary. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(4000)] - [JsonPropertyName("customInstructions")] - public string? CustomInstructions { get; set; } + /// Whether the account can sign up for Copilot Free. + [JsonPropertyName("canSignupForCopilotFree")] + public bool CanSignupForCopilotFree { get; set; } - /// Context window token limit this compaction is targeting, recorded as the `tokenLimit` on the persisted `session.compaction_start` / `session.compaction_complete` events. Set it when the compaction targets a window other than the compacting model's own, e.g. switching to a model with a smaller context window: the compaction still runs on the current model, so the limit that motivated it would otherwise be lost. When absent, the events record the compacting model's own resolved limit. Attribution metadata only - it does not change how much the compaction removes. - [JsonPropertyName("tokenLimit")] - public long? TokenLimit { get; set; } + /// Whether the account can delegate tasks to GitHub. + [JsonPropertyName("delegateAvailable")] + public bool DelegateAvailable { get; set; } + + /// Existing delegation warning for a free account. + [JsonPropertyName("delegateWarning")] + public SessionQuotaDelegateWarning? DelegateWarning { get; set; } + + /// Whether dynamic workflows are enabled for the active account. + [JsonPropertyName("dynamicWorkflowsEnabled")] + public bool DynamicWorkflowsEnabled { get; set; } + + /// Whether dynamic workflows are visible under the session's feature flags. + [JsonPropertyName("dynamicWorkflowsUiVisible")] + public bool DynamicWorkflowsUiVisible { get; set; } + + /// Whether the account uses the free limited Copilot plan. + [JsonPropertyName("isFreeUser")] + public bool IsFreeUser { get; set; } + + /// Whether the account uses token-based billing. + [JsonPropertyName("isTbbUser")] + public bool IsTbbUser { get; set; } + + /// Whether model cost columns should be shown. + [JsonPropertyName("modelCostColumnVisible")] + public bool ModelCostColumnVisible { get; set; } + + /// Resolved Copilot plan tier. + [JsonPropertyName("planTier")] + public SessionQuotaPlanTier PlanTier { get; set; } + + /// Whether premium-request costs are billable. + [JsonPropertyName("premiumRequestsBillable")] + public bool PremiumRequestsBillable { get; set; } + + /// Provider-owned account quota state, including snapshots without numeric balances. + [JsonPropertyName("providerQuotas")] + public IList? ProviderQuotas { get; set; } - /// What initiated this compaction request, recorded as the `trigger` on the persisted `session.compaction_start` / `session.compaction_complete` events. When absent, the compaction is persisted without trigger attribution (initiator unknown). - [JsonPropertyName("trigger")] - public SessionHistoryCompactRequestTrigger? Trigger { get; set; } + /// Known quota snapshots, keyed by the provider's quota type. + [JsonPropertyName("snapshots")] + public IDictionary Snapshots { get => field ??= new Dictionary(); set; } + + /// Upgrade link for a free account. + [JsonPropertyName("upgradeUrl")] + public string? UpgradeUrl { get; set; } } -/// RPC data type for SessionHistoryCompactRequestWithSession operations. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionHistoryCompactRequestWithSession +internal sealed class SessionQuotaGetRequest { - /// Optional user-provided instructions to focus the compaction summary. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MaxLength(4000)] - [JsonPropertyName("customInstructions")] - public string? CustomInstructions { get; set; } - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// Context window token limit this compaction is targeting, recorded as the `tokenLimit` on the persisted `session.compaction_start` / `session.compaction_complete` events. Set it when the compaction targets a window other than the compacting model's own, e.g. switching to a model with a smaller context window: the compaction still runs on the current model, so the limit that motivated it would otherwise be lost. When absent, the events record the compacting model's own resolved limit. Attribution metadata only - it does not change how much the compaction removes. - [JsonPropertyName("tokenLimit")] - public long? TokenLimit { get; set; } - - /// What initiated this compaction request, recorded as the `trigger` on the persisted `session.compaction_start` / `session.compaction_complete` events. When absent, the compaction is persisted without trigger attribution (initiator unknown). - [JsonPropertyName("trigger")] - public SessionHistoryCompactRequestTrigger? Trigger { get; set; } } -/// Number of events that were removed by the truncation. +/// The incumbent session-owned quota and account projection. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistoryTruncateResult +public sealed class SessionQuotaRefreshResult { - /// Failure detail when checkpointCleanupFailed is true. - [JsonPropertyName("checkpointCleanupError")] - public string? CheckpointCleanupError { get; set; } + /// Whether the account can sign up for Copilot Free. + [JsonPropertyName("canSignupForCopilotFree")] + public bool CanSignupForCopilotFree { get; set; } - /// True when conversation truncation succeeded but post-truncation workspace checkpoint cleanup failed. History is already truncated; callers may still prune snapshots but should report a checkpoint-cleanup rather than a truncation failure. - [JsonPropertyName("checkpointCleanupFailed")] - public bool? CheckpointCleanupFailed { get; set; } + /// Whether the account can delegate tasks to GitHub. + [JsonPropertyName("delegateAvailable")] + public bool DelegateAvailable { get; set; } - /// Number of events that were removed. - [JsonPropertyName("eventsRemoved")] - public long EventsRemoved { get; set; } -} + /// Existing delegation warning for a free account. + [JsonPropertyName("delegateWarning")] + public SessionQuotaDelegateWarning? DelegateWarning { get; set; } -/// Identifier of the event to truncate to; this event and all later events are removed. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HistoryTruncateRequest -{ - /// Event ID to truncate to. This event and all events after it are removed from the session. - [JsonPropertyName("eventId")] - public string EventId { get; set; } = string.Empty; + /// Whether dynamic workflows are enabled for the active account. + [JsonPropertyName("dynamicWorkflowsEnabled")] + public bool DynamicWorkflowsEnabled { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Whether dynamic workflows are visible under the session's feature flags. + [JsonPropertyName("dynamicWorkflowsUiVisible")] + public bool DynamicWorkflowsUiVisible { get; set; } -/// A root user turn that the session can rewind to. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistoryRewindPoint -{ - /// Whether at least one file in this turn or a later turn can be restored. - [JsonPropertyName("canRestoreFiles")] - public bool CanRestoreFiles { get; set; } + /// Whether the account uses the free limited Copilot plan. + [JsonPropertyName("isFreeUser")] + public bool IsFreeUser { get; set; } - /// ID of the user.message event that begins the discarded suffix. - [JsonPropertyName("eventId")] - public string EventId { get; set; } = string.Empty; + /// Whether the account uses token-based billing. + [JsonPropertyName("isTbbUser")] + public bool IsTbbUser { get; set; } - /// Number of unique files in this turn and all later turns that have captured changes. - [JsonPropertyName("fileCount")] - public long FileCount { get; set; } + /// Whether model cost columns should be shown. + [JsonPropertyName("modelCostColumnVisible")] + public bool ModelCostColumnVisible { get; set; } - /// Whether this turn was an automatically injected autopilot continuation. - [JsonPropertyName("isAutopilotContinuation")] - public bool IsAutopilotContinuation { get; set; } + /// Resolved Copilot plan tier. + [JsonPropertyName("planTier")] + public SessionQuotaPlanTier PlanTier { get; set; } - /// Lines added by this turn's captured file changes. - [JsonPropertyName("linesAdded")] - public long LinesAdded { get; set; } + /// Whether premium-request costs are billable. + [JsonPropertyName("premiumRequestsBillable")] + public bool PremiumRequestsBillable { get; set; } - /// Lines removed by this turn's captured file changes. - [JsonPropertyName("linesRemoved")] - public long LinesRemoved { get; set; } + /// Provider-owned account quota state, including snapshots without numeric balances. + [JsonPropertyName("providerQuotas")] + public IList? ProviderQuotas { get; set; } - /// ISO timestamp of the user turn. - [JsonPropertyName("timestamp")] - public string Timestamp { get; set; } = string.Empty; + /// Known quota snapshots, keyed by the provider's quota type. + [JsonPropertyName("snapshots")] + public IDictionary Snapshots { get => field ??= new Dictionary(); set; } - /// Whether this turn itself captured any file changes. - [JsonPropertyName("turnChangedFiles")] - public bool TurnChangedFiles { get; set; } + /// Upgrade link for a free account. + [JsonPropertyName("upgradeUrl")] + public string? UpgradeUrl { get; set; } +} - /// User-visible message text for the turn. - [JsonPropertyName("userMessage")] - public string UserMessage { get; set; } = string.Empty; +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionQuotaRefreshRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Rewind points and file-change-tracking availability for the session. +/// A pending session quota warning, consumed once by takeWarnings. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistoryListRewindPointsResult +public sealed class QuotaWarningProjection { - /// Whether this session captured file changes from its first turn. - [JsonPropertyName("fileChangeTrackingEnabled")] - public bool FileChangeTrackingEnabled { get; set; } + /// User-facing warning text. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; - /// Root user turns in chronological order. Empty when `unavailableReason` is set. - [JsonPropertyName("points")] - public IList Points { get => field ??= []; set; } + /// Related account or upgrade link. + [JsonPropertyName("url")] + public string? Url { get; set; } - /// Why the listed points could not be produced, when applicable; the points list is empty whenever it is set. `unsupported-remote-session` is permanent for the session and comes with `fileChangeTrackingEnabled: false`. `session-busy` is transient and only ever reported by a session that *is* tracking (`fileChangeTrackingEnabled: true`), because the file-change captures cannot be read while work that may still mutate them is in flight; the same request succeeds once the session settles, so a client that wants points should retry rather than treat it as a failure. It is never `file-change-tracking-disabled`: an untracked local session still lists conversation-only points and reports that through `fileChangeTrackingEnabled: false`. - [JsonPropertyName("unavailableReason")] - public HistoryRewindUnavailableReason? UnavailableReason { get; set; } + /// Existing warning category. + [JsonPropertyName("warningType")] + public string WarningType { get; set; } = string.Empty; } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionHistoryListRewindPointsRequest +internal sealed class SessionQuotaTakeWarningsRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// A file that a conversation-and-files rewind would restore. +/// Prediction result. Available results include prediction details; unavailable results include an explicit reason. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistoryRewindFilePreview +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(SessionLimitPredictionResultAvailable), "available")] +[JsonDerivedType(typeof(SessionLimitPredictionResultUnavailable), "unavailable")] +public partial class SessionLimitPredictionResult { - /// Aggregate change made across the discarded turns. - [JsonPropertyName("changeType")] - public HistoryRewindChangeType ChangeType { get; set; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// Lines added across the discarded turns. - [JsonPropertyName("linesAdded")] - public long LinesAdded { get; set; } - /// Lines removed across the discarded turns. - [JsonPropertyName("linesRemoved")] - public long LinesRemoved { get; set; } +/// Baseline data provenance for a prediction. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionLimitPredictionBaselineData +{ + /// End of the baseline data slice. + [JsonPropertyName("windowEnd")] + public string WindowEnd { get; set; } = string.Empty; - /// Absolute path of the captured file. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Start of the baseline data slice. + [JsonPropertyName("windowStart")] + public string WindowStart { get; set; } = string.Empty; } -/// Files and aggregate changes for a prospective rewind. +/// Semantic usage tier and its AI-credit cap. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistoryPreviewRewindResult +public sealed class SessionLimitPredictionTierOption { - /// Whether file restore is available for this session. This is authoritative: switch on it and read `reason` only when it is false. - [JsonPropertyName("available")] - public bool Available { get; set; } + /// AI-credit cap for this tier. + [JsonPropertyName("cap")] + public double Cap { get; set; } - /// Number of unique files in the preview. - [JsonPropertyName("fileCount")] - public long FileCount { get; set; } + /// Semantic usage tier. + [JsonPropertyName("tier")] + public SessionLimitPredictionTier Tier { get; set; } +} - /// Files ordered by path. - [JsonPropertyName("files")] - public IList Files { get => field ??= []; set; } +/// Explainable AI-credit session-limit prediction. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionLimitPredictionDetails +{ + /// Baseline data provenance. + [JsonPropertyName("baselineData")] + public SessionLimitPredictionBaselineData BaselineData { get => field ??= new(); set; } - /// Why file restore is unavailable, when applicable. Populated only when `available` is false and never set when `available` is true. - [JsonPropertyName("reason")] - public HistoryRewindUnavailableReason? Reason { get; set; } + /// Client population used for the prediction. + [JsonPropertyName("clientType")] + public SessionLimitPredictionClientType ClientType { get; set; } + + /// Resolved model family when known. + [JsonPropertyName("family")] + public string? Family { get; set; } + + /// Model identifier used for lookup. + [JsonPropertyName("modelId")] + public string ModelId { get; set; } = string.Empty; + + /// Recommended maximum AI credits for this session. + [JsonPropertyName("recommendedCap")] + public double RecommendedCap { get; set; } + + /// Tier chosen as the recommended cap. + [JsonPropertyName("recommendedTier")] + public SessionLimitPredictionTier RecommendedTier { get; set; } + + /// Baseline fallback level used to create the prediction. + [JsonPropertyName("source")] + public SessionLimitPredictionSource Source { get; set; } + + /// Key matched at the source level, such as a model id, family id, or `global`. + [JsonPropertyName("sourceKey")] + public string SourceKey { get; set; } = string.Empty; + + /// Ordered usage tiers and their AI-credit caps. + [JsonPropertyName("tiers")] + public IList Tiers { get => field ??= []; set; } } -/// Event boundary to preview for conversation-and-files rewind. +/// The available variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HistoryPreviewRewindRequest +public partial class SessionLimitPredictionResultAvailable : SessionLimitPredictionResult { - /// ID of the user.message event that begins the discarded suffix. - [JsonPropertyName("eventId")] - public string EventId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "available"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Predicted session limit details. + [JsonPropertyName("prediction")] + public required SessionLimitPredictionDetails Prediction { get; set; } } -/// A captured file that rewind intentionally left unchanged. +/// The unavailable variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistorySkippedFileRestore +public partial class SessionLimitPredictionResultUnavailable : SessionLimitPredictionResult { - /// Absolute path of the skipped file. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "unavailable"; - /// Reason the file was not restored. + /// Reason no prediction is available. [JsonPropertyName("reason")] - public HistoryFileRestoreSkipReason Reason { get; set; } + public required SessionLimitPredictionUnavailableReason Reason { get; set; } } -/// Structured outcome of a rewind request. +/// RPC data type for SessionLimitPredictionPredict operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistoryRewindResult +public sealed class SessionLimitPredictionPredictRequest { - /// Failure detail. Set only for the failure and partial-failure outcomes (`files-rolled-back`, `rollback-incomplete`, `truncation-failed`, `checkpoint-cleanup-failed`, `snapshot-prune-failed`); omitted for `success` and for the unavailable outcomes (`session-busy`, `file-change-tracking-disabled`, `unsupported-remote-session`). - [JsonPropertyName("error")] - public string? Error { get; set; } - - /// Number of persisted events removed by conversation truncation. Present only when truncation succeeded (outcomes `success`, `checkpoint-cleanup-failed`, and `snapshot-prune-failed`); omitted for every unavailable outcome (`session-busy`, `file-change-tracking-disabled`, `unsupported-remote-session`) and for `truncation-failed`, `files-rolled-back`, and `rollback-incomplete`. - [JsonPropertyName("eventsRemoved")] - public long? EventsRemoved { get; set; } - - /// Overall rewind outcome. This discriminates the result: it governs which of the remaining fields are populated, so consumers must switch on it before reading `eventsRemoved`, `restoredFiles`, `skippedFiles`, or `error`. See each field for the outcomes that populate it. - [JsonPropertyName("outcome")] - public HistoryRewindOutcome Outcome { get; set; } - - /// Absolute paths restored to their captured preimages. Always empty for conversation-only rewinds and for the unavailable outcomes (`session-busy`, `file-change-tracking-disabled`, `unsupported-remote-session`); only conversation-and-files outcomes that reached the file-restore stage populate it. - [JsonPropertyName("restoredFiles")] - public IList RestoredFiles { get => field ??= []; set; } + /// Client type to size for. Defaults to `cli-interactive`. + [JsonPropertyName("clientType")] + public SessionLimitPredictionClientType? ClientType { get; set; } - /// Captured files intentionally left unchanged. Always empty for conversation-only rewinds and for the unavailable outcomes (`session-busy`, `file-change-tracking-disabled`, `unsupported-remote-session`); only conversation-and-files outcomes that reached the file-restore stage populate it. - [JsonPropertyName("skippedFiles")] - public IList SkippedFiles { get => field ??= []; set; } + /// Optional model identifier override. If omitted, the session's current model is used. + [JsonPropertyName("modelId")] + public string? ModelId { get; set; } } -/// Boundary and mode for rewinding session history. +/// RPC data type for SessionLimitPredictionPredictRequestWithSession operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HistoryRewindRequest +internal sealed class SessionLimitPredictionPredictRequestWithSession { - /// ID of the user.message event that begins the discarded suffix. - [JsonPropertyName("eventId")] - public string EventId { get; set; } = string.Empty; + /// Client type to size for. Defaults to `cli-interactive`. + [JsonPropertyName("clientType")] + public SessionLimitPredictionClientType? ClientType { get; set; } - /// Whether to rewind only conversation history or also restore captured files. - [JsonPropertyName("mode")] - public HistoryRewindMode Mode { get; set; } + /// Optional model identifier override. If omitted, the session's current model is used. + [JsonPropertyName("modelId")] + public string? ModelId { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether an in-progress background compaction was cancelled. +/// GitHub URL for the session and a flag indicating whether remote steering is enabled. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistoryCancelBackgroundCompactionResult +public sealed class RemoteEnableResult { - /// Whether an in-progress background compaction was cancelled. False when no compaction was running, when the session is remote, or when the underlying processor was unavailable. - [JsonPropertyName("cancelled")] - public bool Cancelled { get; set; } + /// Whether remote steering is enabled. + [JsonPropertyName("remoteSteerable")] + public bool RemoteSteerable { get; set; } + + /// GitHub frontend URL for this session. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("url")] + public string? Url { get; set; } +} + +/// Optional remote session mode ("off", "export", or "on"); defaults to enabling both export and remote steering. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class RemoteEnableRequest +{ + /// Per-session remote mode. "off" disables remote, "export" exports session events to GitHub without enabling remote steering, "on" enables both export and remote steering. + [JsonPropertyName("mode")] + public RemoteSessionMode? Mode { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionHistoryCancelBackgroundCompactionRequest +internal sealed class SessionRemoteDisableRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether an in-progress manual compaction was aborted. +/// 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. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistoryAbortManualCompactionResult +public sealed class RemoteNotifySteerableChangedResult { - /// Whether an in-progress manual compaction was aborted. False when no manual compaction was running, when its abort controller was already aborted, or when the session is remote. - [JsonPropertyName("aborted")] - public bool Aborted { get; set; } } -/// Identifies the target session. +/// New remote-steerability state to persist as a `session.remote_steerable_changed` event. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionHistoryAbortManualCompactionRequest +internal sealed class RemoteNotifySteerableChangedRequest { + /// Whether the session now supports remote steering via GitHub. The runtime persists this as a `session.remote_steerable_changed` event so resume/replay sees the up-to-date capability. + [JsonPropertyName("remoteSteerable")] + public bool RemoteSteerable { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Markdown summary of the conversation context (empty when not available). +/// Current sharing status and shareable GitHub URL for a session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistorySummarizeForHandoffResult +public sealed class VisibilityGetResult { - /// Markdown summary of the conversation context produced by an LLM. Empty string when there are no messages or when the session does not support local summarization. - [JsonPropertyName("summary")] - public string Summary { get; set; } = string.Empty; + /// Shareable GitHub URL for the session. Present when the session is synced and the URL can be resolved. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("shareUrl")] + public string? ShareUrl { get; set; } + + /// Current sharing status. Absent when the session is not synced or the status could not be retrieved (e.g. the user is not authenticated). + [JsonPropertyName("status")] + public SessionVisibilityStatus? Status { get; set; } + + /// Whether the session has been synced to Mission Control (i.e. has a GitHub task). When false, the session cannot be shared and `status`/`shareUrl` are absent. + [JsonPropertyName("synced")] + public bool Synced { get; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionHistorySummarizeForHandoffRequest +internal sealed class SessionVisibilityGetRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// What a successful clear removed. A clear that could not be applied rejects instead of reporting a count. +/// Effective sharing status and shareable GitHub URL after updating session visibility. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistoryClearContextResult +public sealed class VisibilitySetResult { - /// Number of non-system, non-developer messages that were removed from the conversation. Zero only when the window already held no conversation. - [JsonPropertyName("messagesCleared")] - public long MessagesCleared { get; set; } + /// Shareable GitHub URL for the session. Present when the session is synced and the URL can be resolved. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("shareUrl")] + public string? ShareUrl { get; set; } + + /// Effective sharing status after the update. May differ from the requested status for task types that are already visible to repository readers by default. Absent when the update could not be applied (e.g. the session is not synced or the user is not authenticated). + [JsonPropertyName("status")] + public SessionVisibilityStatus? Status { get; set; } + + /// Whether the session has been synced to Mission Control (i.e. has a GitHub task). When false, the visibility change could not be applied and `status`/`shareUrl` are absent. + [JsonPropertyName("synced")] + public bool Synced { get; set; } } -/// Parameters for clearing the conversation and seeding the window that replaces it. +/// Desired sharing status for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HistoryClearContextRequest +internal sealed class VisibilitySetRequest { - /// First user message of the fresh context window. Required: a cleared window holding only system and developer messages is not a conversation a model can answer, so every clear seeds the window it creates. Delivered by the enclosing turn driver once the agentic loop exits, which is why the call must be made from inside a tool handler. - [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Sharing status to apply. "repo" makes the session visible to repository readers; "unshared" restricts it to the creator and collaborators. + [JsonPropertyName("status")] + public SessionVisibilityStatus Status { get; set; } } -/// User-facing pending queue entry, with kind and display text for a queued message, slash command, or model change. +/// Scheduled prompt entry with ID, timing (`intervalMs`, `cron`, or `at`), prompt text, recurrence, and next run time. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueuePendingItems +public sealed class ScheduleEntry { - /// Agent mode stored on this queued entry, as stamped when it was enqueued. Items without an explicit mode report interactive. This is not necessarily the mode that will constrain the turn: a plan or autopilot session applies its own write gate, continuation loop and permission posture to every drained item regardless of the mode stored here. - [JsonPropertyName("agentMode")] - public SendAgentMode AgentMode { get; set; } + /// Absolute fire time (epoch milliseconds) for a one-shot calendar schedule. + [JsonPropertyName("at")] + public long? At { get; set; } - /// Human-readable text to display for this queue entry in the UI. - [JsonPropertyName("displayText")] - public string DisplayText { get; set; } = string.Empty; + /// 5-field cron expression for a recurring calendar schedule, evaluated in `tz`. + [JsonPropertyName("cron")] + public string? Cron { get; set; } - /// Stable opaque id for the canonical queued item. Batch rows share one id. + /// Display-only label for the prompt as shown in the UI (e.g. `/skill-name` for a skill-invocation schedule). The actual enqueued prompt is `prompt`. + [JsonPropertyName("displayPrompt")] + public string? DisplayPrompt { get; set; } + + /// Sequential id assigned by the runtime within the session. Stable across resumes (rebuilt from the event log). [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + public long Id { get; set; } - /// Whether this item is a queued user message or a queued slash command / model change. - [JsonPropertyName("kind")] - public QueuePendingItemsKind Kind { get; set; } + /// Interval between scheduled ticks, in milliseconds (relative-interval schedules). + [JsonConverter(typeof(MillisecondsTimeSpanConverter))] + [JsonPropertyName("intervalMs")] + public TimeSpan? Interval { get; set; } - /// Stable identity of the queued user message. Present for message rows and absent for slash commands and model changes. - [JsonPropertyName("messageId")] - public string? MessageId { get; set; } + /// ISO 8601 timestamp when the next tick is scheduled to fire. + [JsonPropertyName("nextRunAt")] + public DateTimeOffset NextRunAt { get; set; } - /// Optional source tag associated with this pending queue entry. This is an open string, not authenticated authorship. In particular, `user` does not prove that a person typed the message. If the source is absent or unrecognized, consumers must not infer human or agent authorship and should handle the entry neutrally. Consumers should tolerate future source values. - [JsonPropertyName("source")] - public string? Source { get; set; } + /// Prompt text that gets enqueued on every tick. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; + + /// Whether the schedule re-arms after each tick (`/every`) or fires once (`/after`). + [JsonPropertyName("recurring")] + public bool Recurring { get; set; } + + /// True for a self-paced (`dynamic`) schedule: no fixed cadence; the model arms each next run via the `manage_schedule` `wakeup` action. `nextRunAt` is model-controlled. + [JsonPropertyName("selfPaced")] + public bool? SelfPaced { get; set; } + + /// IANA timezone the `cron` expression is evaluated in. + [JsonPropertyName("tz")] + public string? Tz { get; set; } } -/// Snapshot of the session's pending queued items and immediate-steering messages. +/// Snapshot of the currently active recurring prompts for this session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueuePendingItemsResult +public sealed class ScheduleList { - /// How many leading entries of `steeringMessages` have already been folded into the running turn (and so have an emitted `user.message`), as opposed to still waiting for one. Absent for hosts that do not distinguish the two. - [JsonPropertyName("inFlightSteeringCount")] - public long? InFlightSteeringCount { get; set; } - - /// Pending queued items in submission order. Includes user messages, queued slash commands, and queued model changes; omits internal system items. - [JsonPropertyName("items")] - public IList Items { get => field ??= []; set; } - - /// Display text for messages currently in the immediate steering queue (interjections sent during a running turn). - [JsonPropertyName("steeringMessages")] - public IList SteeringMessages { get => field ??= []; set; } + /// Active scheduled prompts, ordered by id. + [JsonPropertyName("entries")] + public IList Entries { get => field ??= []; set; } +} - /// ID of the running turn's user message while the model has not answered it, so `withdrawMessage` can still take it back once nothing sent after it is pending. A message leaves `items` when its turn starts, before its `user.message` is recorded; this tells that message apart from one that was removed. Absent when no turn prompt can be taken back. - [JsonPropertyName("withdrawableTurnMessageId")] - public string? WithdrawableTurnMessageId { get; set; } +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionScheduleListRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionQueuePendingItemsRequest +internal sealed class SessionScheduleHydrateRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Internal snapshot of native queue state for local session orchestration. +/// Whether the session currently has an active self-paced schedule. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueSnapshotResult +internal sealed class ScheduleHasSelfPacedResult { - /// Queue item identifier of a model switch that has been dequeued but not yet applied. - [JsonPropertyName("inFlightModelChangeId")] - public string? InFlightModelChangeId { get; set; } - - /// Insertion orders for queued items, aligned with `items`. - [JsonPropertyName("itemOrders")] - public IList? ItemOrders { get; set; } - - /// User-facing pending items in FIFO order. - [JsonPropertyName("items")] - public IList Items { get => field ??= []; set; } - - /// Insertion orders for immediate steering messages, aligned with `steeringMessages`. - [JsonPropertyName("steeringMessageOrders")] - public IList? SteeringMessageOrders { get; set; } - - /// Immediate steering messages waiting for an active turn. - [JsonPropertyName("steeringMessages")] - public IList SteeringMessages { get => field ??= []; set; } + /// True when at least one active schedule is self-paced. + [JsonPropertyName("hasSelfPaced")] + public bool HasSelfPaced { get; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionQueueSnapshotRequest +internal sealed class SessionScheduleHasSelfPacedRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of moving a queued item. +/// Result of registering or re-arming a scheduled prompt. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueueMoveItemResult +internal sealed class ScheduleAddResult { - /// True when the item changed position; false when it was already at the requested position. - [JsonPropertyName("changed")] - public bool Changed { get; set; } + /// The registered or updated schedule entry. + [JsonPropertyName("entry")] + public ScheduleEntry? Entry { get; set; } + + /// User-facing validation error, when registration failed. + [JsonPropertyName("error")] + public string? Error { get; set; } } -/// Parameters for moving a queued item by stable id. +/// Register a relative-interval scheduled prompt. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueMoveItemRequest +internal sealed class ScheduleAddRequest { - /// Stable opaque queued-item id. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Optional display-only prompt label. + [JsonPropertyName("displayPrompt")] + public string? DisplayPrompt { get; set; } + + /// Human-readable interval such as `30s`, `5m`, or `2h`. + [JsonPropertyName("interval")] + public string Interval { get; set; } = string.Empty; + + /// Prompt text to enqueue when the schedule fires. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; + + /// Whether the schedule should re-arm after each tick. Defaults to true. + [JsonPropertyName("recurring")] + public bool? Recurring { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// Zero-based target position in the public visible queue. Values outside the queue clamp to an end. - [JsonPropertyName("toPosition")] - public long ToPosition { get; set; } } -/// Result of inserting a queued message. +/// Register a cron scheduled prompt. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueueInsertAtResult +internal sealed class ScheduleAddCronRequest { - /// Fresh stable opaque id assigned to the inserted item. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// 5-field cron expression. + [JsonPropertyName("cron")] + public string Cron { get; set; } = string.Empty; + + /// Optional display-only prompt label. + [JsonPropertyName("displayPrompt")] + public string? DisplayPrompt { get; set; } + + /// Prompt text to enqueue when the schedule fires. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; + + /// Whether the schedule should re-arm after each tick. Defaults to true. + [JsonPropertyName("recurring")] + public bool? Recurring { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; + + /// IANA timezone for evaluating the cron expression. + [JsonPropertyName("tz")] + public string? Tz { get; set; } } -/// Serializable message fields accepted by queue.insertAt. +/// Register an absolute-time scheduled prompt. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueueInsertMessage +internal sealed class ScheduleAddAtRequest { - /// Optional explicit agent mode. When omitted, the session's current mode is assigned. - [JsonPropertyName("agentMode")] - public SendAgentMode? AgentMode { get; set; } + /// Epoch milliseconds when the prompt should fire. + [JsonPropertyName("at")] + public long At { get; set; } - /// Optional attachments for the message. - [JsonPropertyName("attachments")] - public IList? Attachments { get; set; } + /// Optional display-only prompt label. + [JsonPropertyName("displayPrompt")] + public string? DisplayPrompt { get; set; } - /// Whether the message is billable. - [JsonPropertyName("billable")] - public bool? Billable { get; set; } + /// Prompt text to enqueue when the schedule fires. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; - /// Accepted for internal SendOptions compatibility but ignored; delivery is derived from current session activity. - [JsonPropertyName("delivery")] - public string? Delivery { get; set; } + /// Whether the schedule should re-arm after each tick. Defaults to false. + [JsonPropertyName("recurring")] + public bool? Recurring { get; set; } - /// Optional user-facing display text. + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Register a self-paced scheduled prompt. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ScheduleAddSelfPacedRequest +{ + /// Optional display-only prompt label. [JsonPropertyName("displayPrompt")] public string? DisplayPrompt { get; set; } - /// Accepted for SendOptions compatibility but ignored; inserted items always use queued delivery semantics. - [JsonPropertyName("mode")] - public SendMode? Mode { get; set; } - - /// Accepted for SendOptions compatibility but ignored; the requested public position controls placement. - [JsonPropertyName("prepend")] - public bool? Prepend { get; set; } - - /// The user message text. + /// Prompt text to enqueue when the schedule fires. [JsonPropertyName("prompt")] public string Prompt { get; set; } = string.Empty; - /// Per-turn request headers. - [JsonPropertyName("requestHeaders")] - public IDictionary? RequestHeaders { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Required tool name for the turn, when any. - [JsonPropertyName("requiredTool")] - public string? RequiredTool { get; set; } +/// Re-arm a self-paced scheduled prompt. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ScheduleRearmSelfPacedRequest +{ + /// Epoch milliseconds when the prompt should next fire. + [JsonPropertyName("at")] + public long At { get; set; } - /// Optional provenance source. `system` is rejected: it would hide the inserted row from `pendingItems` and make it unaddressable while still executing, so inserted items must stay visible. - [JsonPropertyName("source")] - public string? Source { get; set; } + /// Id of the self-paced scheduled prompt. + [JsonPropertyName("id")] + public long Id { get; set; } - /// Accepted for SendOptions compatibility but ignored; insertion scheduling is controlled by the queue drain state. - [JsonPropertyName("wait")] - public bool? Wait { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Parameters for inserting a queued message at a public visible position. +/// Remove a scheduled prompt by id. The result entry is omitted if the id was unknown. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueInsertAtRequest +public sealed class ScheduleStopResult { - /// Queued message contents and delivery metadata. - [JsonPropertyName("message")] - public QueueInsertMessage Message { get => field ??= new(); set; } + /// The removed entry, or omitted if no entry matched. + [JsonPropertyName("entry")] + public ScheduleEntry? Entry { get; set; } +} - /// Zero-based position in the public visible queue. Values outside the queue clamp to an end. - [JsonPropertyName("position")] - public long Position { get; set; } +/// Identifier of the scheduled prompt to remove. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ScheduleStopRequest +{ + /// Id of the scheduled prompt to remove. + [JsonPropertyName("id")] + public long Id { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of removing a queued item. +/// A bearer token supplied by the SDK client for a BYOK provider. The runtime sets it as `Authorization: Bearer <token>` on the outbound request and does no caching; the SDK consumer owns token caching and refresh. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueueRemoveAtResult +public sealed class ProviderTokenAcquireResult { - /// True when the addressed item was removed. - [JsonPropertyName("removed")] - public bool Removed { get; set; } + /// The bearer token value (without the `Bearer ` prefix). + [JsonPropertyName("token")] + public string Token { get; set; } = string.Empty; } -/// Parameters for removing a queued item by stable id. +/// Asks the SDK client to acquire a bearer token for a BYOK provider whose config set `hasBearerTokenProvider: true`. Issued by the runtime before each outbound model request; the runtime does no caching, so this is sent once per request. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueRemoveAtRequest +public sealed class ProviderTokenAcquireRequest { - /// Stable opaque ID of the queued item to remove. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Name of the BYOK provider needing a token. For the legacy whole-session provider this is the implicit provider name; for named providers it is the configured provider name. + [JsonPropertyName("providerName")] + public string ProviderName { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of editing a queued message. +/// Result returned by an extension workflow closure. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueueUpdateTextResult +public sealed class WorkflowExecuteResult { - /// True when the stored text changed. - [JsonPropertyName("updated")] - public bool Updated { get; set; } + /// Workflow result value. + [JsonPropertyName("result")] + public JsonElement? Result { get; set; } } -/// Parameters for editing a single queued message. +/// Parameters sent to the owning extension to execute a workflow closure. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueUpdateTextRequest +public sealed class WorkflowExecuteRequest { - /// Optional replacement prompt displayed to the user. - [JsonPropertyName("displayPrompt")] - public string? DisplayPrompt { get; set; } + /// Workflow input value. + [JsonPropertyName("args")] + public JsonElement Args { get; set; } - /// Stable opaque ID of the queued item to edit. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Opaque token identifying this workflow execution attempt. + [JsonPropertyName("executionToken")] + public string ExecutionToken { get; set; } = string.Empty; - /// Replacement prompt sent to the model. - [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; + /// Registered workflow name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of withdrawing a user message. +/// Parameters for cooperatively aborting a workflow body. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueueWithdrawMessageResult +public sealed class WorkflowAbortRequest { - /// True when the running turn was interrupted to withdraw the message. With removed false, the turn was interrupted but its events could not be removed, for example because the model answered first, so the message stays in the interrupted turn. - [JsonPropertyName("interrupted")] - public bool Interrupted { get; set; } + /// Opaque token identifying the execution attempt to abort. + [JsonPropertyName("executionToken")] + public string ExecutionToken { get; set; } = string.Empty; - /// True when the message left the queue or, for a running turn, history. - [JsonPropertyName("removed")] - public bool Removed { get; set; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Conditional withdrawal of a single user message, from its queue or from the running turn it started. +/// Whether the client authoritatively confirmed its external work stopped. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueWithdrawMessageRequest +public sealed class ClientTaskCancelResult { - /// The prompt originally sent. A message edited since submission is not withdrawn, so an obsolete draft cannot replace the edit. - [JsonPropertyName("expectedPrompt")] - public string ExpectedPrompt { get; set; } = string.Empty; + /// True only when the owner confirms that external work stopped before responding. + [JsonPropertyName("cancelled")] + public bool Cancelled { get; set; } +} - /// Message identity returned by send, not the queue item id. Batch messages are not eligible. - [JsonPropertyName("messageId")] - public string MessageId { get; set; } = string.Empty; +/// Runtime-to-owner cancellation request for a client-owned task. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ClientTaskCancelRequest +{ + /// Opaque identifier shared by coalesced cancellation callers. + [JsonPropertyName("cancellationId")] + public string CancellationId { get; set; } = string.Empty; - /// Target session identifier. + /// Owner-scoped task key included for correlation. + [JsonPropertyName("clientTaskId")] + public string ClientTaskId { get; set; } = string.Empty; + + /// Canonical runtime-generated task identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Reason the runtime requests cancellation. + [JsonPropertyName("reason")] + public ClientTaskCancelReason Reason { get; set; } + + /// Session that owns the client task. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Append to one pending steering message without changing its identity or delivery position. +/// Describes a filesystem error. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueAppendSteeringRequest +public sealed class SessionFsError { - /// Mode captured at submission. Only steering messages in the same mode may be combined. - [JsonPropertyName("agentMode")] - public SendAgentMode AgentMode { get; set; } + /// Error classification. + [JsonPropertyName("code")] + public SessionFsErrorCode Code { get; set; } - /// Attachments to add after the message's existing attachments. An empty list preserves the existing attachments. - [JsonPropertyName("attachments")] - public IList Attachments { get => field ??= []; set; } + /// Free-form detail about the error, for logging/diagnostics. + [JsonPropertyName("message")] + public string? Message { get; set; } - /// Display text to append to the existing preview after a blank line. - [JsonPropertyName("displayPrompt")] - public string DisplayPrompt { get; set; } = string.Empty; + /// For failed writeFile requests only: true if the provider changed the target before failing. Omit when unknown or unchanged. + [JsonPropertyName("writeChanged")] + public bool? WriteChanged { get; set; } +} - /// Expected current prompt, including any previous appends. The runtime applies plan-mode normalization before comparing and refuses a changed message. - [JsonPropertyName("expectedPrompt")] - public string ExpectedPrompt { get; set; } = string.Empty; +/// File content as a UTF-8 string, or a filesystem error if the read failed. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionFsReadFileResult +{ + /// File content as UTF-8 string. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; - /// Message identity returned by send, not the queue item id. Only unclaimed user steering messages are eligible. - [JsonPropertyName("messageId")] - public string MessageId { get; set; } = string.Empty; + /// Describes a filesystem error. + [JsonPropertyName("error")] + public SessionFsError? Error { get; set; } +} - /// Text to append after a blank line. - [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; +/// Path of the file to read from the client-provided session filesystem. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionFsReadFileRequest +{ + /// Path using SessionFs conventions. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of duplicating a queued item. +/// File bytes as standard base64, or a filesystem error if the read failed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueueDuplicateAtResult +public sealed class SessionFsReadFileBytesResult { - /// Fresh stable opaque id assigned to the duplicate. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Exact file bytes encoded as standard base64. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; + + /// Describes a filesystem error. + [JsonPropertyName("error")] + public SessionFsError? Error { get; set; } } -/// Parameters for duplicating a queued item. +/// Path of the binary file to read from the client-provided session filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueDuplicateAtRequest +public sealed class SessionFsReadFileBytesRequest { - /// Stable opaque ID of the queued item to duplicate. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Path using SessionFs conventions. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Parameters for acquiring or releasing the queued-lane drain pause. Acquisition is exclusive and non-idempotent: `paused: true` against an already-paused session fails with `queue_already_paused`. The pause is never released automatically — it is not tied to the caller's lifetime, so a client that exits without sending `paused: false` leaves the lane frozen. Release is unowned: `paused: false` clears the pause for any caller, including one that never acquired it. +/// File path, content to write, and optional mode for the client-provided session filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueSetDrainPausedRequest +public sealed class SessionFsWriteFileRequest { - /// Whether queued-lane draining should be paused. - [JsonPropertyName("paused")] - public bool Paused { get; set; } + /// Content to write. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; + + /// Optional POSIX-style mode for newly created files. + [JsonPropertyName("mode")] + public long? Mode { get; set; } + + /// Path using SessionFs conventions. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of trying to steer a queued message into a live turn. +/// File path, standard-base64-encoded bytes to write, and optional mode for the client-provided session filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueueSendNowResult +public sealed class SessionFsWriteFileBytesRequest { - /// True when the item was accepted into the steering lane; false when no main turn was live. - [JsonPropertyName("steered")] - public bool Steered { get; set; } -} + /// Exact file bytes encoded as standard base64. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; -/// Parameters for steering a queued message into a live turn. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueSendNowRequest -{ - /// Stable opaque ID of the queued item to steer into the live turn. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Optional POSIX-style mode for newly created files. + [JsonPropertyName("mode")] + public long? Mode { get; set; } + + /// Path using SessionFs conventions. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Whether the native queue has pending work. +/// File path, content to append, and optional mode for the client-provided session filesystem. Implementations create parent directories as needed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueHasPendingResult +public sealed class SessionFsAppendFileRequest { - /// True when queued or immediate native work is pending. - [JsonPropertyName("hasPending")] - public bool HasPending { get; set; } -} + /// Content to append. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; + + /// Optional POSIX-style mode for newly created files. + [JsonPropertyName("mode")] + public long? Mode { get; set; } + + /// Path using SessionFs conventions. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionQueueHasPendingRequest -{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Whether a deferred-idle drain should run. +/// Indicates whether the requested path exists in the client-provided session filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueBeginDeferredIdleDrainResult +public sealed class SessionFsExistsResult { - /// True when the host should run finishDeferredIdleDrain asynchronously. - [JsonPropertyName("shouldDrain")] - public bool ShouldDrain { get; set; } + /// Whether the path exists. + [JsonPropertyName("exists")] + public bool Exists { get; set; } } -/// Inputs for starting a deferred-idle drain. +/// Path to test for existence in the client-provided session filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueBeginDeferredIdleDrainRequest +public sealed class SessionFsExistsRequest { - /// Whether the host still has active background work. - [JsonPropertyName("activeBackgroundWork")] - public bool ActiveBackgroundWork { get; set; } + /// Path using SessionFs conventions. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Action selected by the native deferred-idle drain. +/// Filesystem metadata for the requested path, or a filesystem error if the stat failed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueFinishDeferredIdleDrainResult +public sealed class SessionFsStatResult { - /// Whether the deferred idle was caused by an aborted foreground turn. - [JsonPropertyName("aborted")] - public bool Aborted { get; set; } + /// ISO 8601 timestamp of creation. + [JsonPropertyName("birthtime")] + public DateTimeOffset Birthtime { get; set; } - /// One of none, processQueue, or emitSessionIdle. - [JsonPropertyName("action")] - public string Action { get; set; } = string.Empty; + /// Describes a filesystem error. + [JsonPropertyName("error")] + public SessionFsError? Error { get; set; } + + /// Whether the path is a directory. + [JsonPropertyName("isDirectory")] + public bool IsDirectory { get; set; } + + /// Whether the path is a file. + [JsonPropertyName("isFile")] + public bool IsFile { get; set; } + + /// ISO 8601 timestamp of last modification. + [JsonPropertyName("mtime")] + public DateTimeOffset Mtime { get; set; } + + /// File size in bytes. + [JsonPropertyName("size")] + public long Size { get; set; } } -/// Inputs for completing a deferred-idle drain. +/// Path whose metadata should be returned from the client-provided session filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueFinishDeferredIdleDrainRequest -{ - /// Whether the host still has active background work. - [JsonPropertyName("activeBackgroundWork")] - public bool ActiveBackgroundWork { get; set; } - - /// Whether native queued work remains. - [JsonPropertyName("hasPending")] - public bool HasPending { get; set; } +public sealed class SessionFsStatRequest +{ + /// Path using SessionFs conventions. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Inputs for marking session.idle deferred in native state. +/// Directory path to create in the client-provided session filesystem, with options for recursive creation and POSIX mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueDeferSessionIdleRequest +public sealed class SessionFsMkdirRequest { - /// Whether the deferred idle was caused by an aborted foreground turn. - [JsonPropertyName("aborted")] - public bool Aborted { get; set; } + /// Optional POSIX-style mode for newly created directories. + [JsonPropertyName("mode")] + public long? Mode { get; set; } + + /// Path using SessionFs conventions. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + + /// Create parent directories as needed. + [JsonPropertyName("recursive")] + public bool? Recursive { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether a user-facing pending item was removed. +/// Names of entries in the requested directory, or a filesystem error if the read failed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueueRemoveMostRecentResult +public sealed class SessionFsReaddirResult { - /// True if a user-facing pending item was removed (LIFO across both queues); false when no removable items remained. - [JsonPropertyName("removed")] - public bool Removed { get; set; } -} + /// Entry names in the directory. + [JsonPropertyName("entries")] + public IList Entries { get => field ??= []; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionQueueRemoveMostRecentRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Describes a filesystem error. + [JsonPropertyName("error")] + public SessionFsError? Error { get; set; } } -/// Identifies the target session. +/// Directory path whose entries should be listed from the client-provided session filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionQueueClearRequest +public sealed class SessionFsReaddirRequest { + /// Path using SessionFs conventions. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Internal filter for consuming queued system notifications. +/// Directory entry returned by session filesystem `readdirWithTypes`, with name and entry type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueConsumeSystemNotificationsRequest +public sealed class SessionFsReaddirWithTypesEntry { - /// Opaque runtime-owned filter object. - [JsonPropertyName("filter")] - public JsonElement Filter { get; set; } + /// Entry name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Entry type. + [JsonPropertyName("type")] + public SessionFsReaddirWithTypesEntryType Type { get; set; } } -/// Result of enqueueing the resume-pending wake item. +/// Entries in the requested directory paired with file/directory type information, or a filesystem error if the read failed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueEnqueueResumePendingResult +public sealed class SessionFsReaddirWithTypesResult { - /// True when a wake item was newly queued. - [JsonPropertyName("queued")] - public bool Queued { get; set; } + /// Directory entries with type information. + [JsonPropertyName("entries")] + public IList Entries { get => field ??= []; set; } + + /// Describes a filesystem error. + [JsonPropertyName("error")] + public SessionFsError? Error { get; set; } } -/// Identifies the target session. +/// Directory path whose entries (with type information) should be listed from the client-provided session filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionQueueEnqueueResumePendingRequest +public sealed class SessionFsReaddirWithTypesRequest { + /// Path using SessionFs conventions. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Identifies the target session. +/// Path to remove from the client-provided session filesystem, with options for recursive removal and force. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionQueueProcessRequest +public sealed class SessionFsRmRequest { + /// Ignore errors if the path does not exist. + [JsonPropertyName("force")] + public bool? Force { get; set; } + + /// Path using SessionFs conventions. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + + /// Remove directories and their contents recursively. + [JsonPropertyName("recursive")] + public bool? Recursive { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Cursor, batch size, and optional long-poll/filter parameters for reading session events. +/// Source and destination paths for renaming or moving an entry in the client-provided session filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class EventLogReadRequest +public sealed class SessionFsRenameRequest { - /// Optional non-empty list of subagent identifiers. When provided, only events owned by one of these agents are returned; ownership recognizes the event envelope's agentId plus legacy data.agentId and data.parentToolCallId markers. This filter takes precedence over agentScope. - [JsonPropertyName("agentIds")] - public IList? AgentIds { get; set; } - - /// Agent-scope filter: 'primary' returns only main-agent events plus events whose type starts with 'subagent.' (matching the typed-subscription default behavior); 'all' returns events from all agents (matching wildcard-subscription behavior). Default is 'all' to preserve wildcard semantics for catch-up callers. - [JsonPropertyName("agentScope")] - public EventsAgentScope? AgentScope { get; set; } - - /// Opaque cursor returned by a previous read. Omit on the first call to start from the beginning of the session's persisted history. - [JsonPropertyName("cursor")] - public string? Cursor { get; set; } - - /// Direction to page through the session's persisted event history. 'forward' (default) pages from the cursor toward newer events (or from the start of history when no cursor is given). 'backward' enables tail-first reads: with no cursor it returns the NEWEST `max` events, and the returned cursor pages toward OLDER events on subsequent backward reads. Events within a returned batch are always in chronological (oldest-to-newest) order, even for a backward read. Backward reads cover PERSISTED history only; ephemeral events are never returned by a backward read. `direction` selects the INITIAL read only: the returned cursor is self-describing, so a continuation read pages in the cursor's own direction regardless of the `direction` passed alongside it — a forward cursor always pages forward and a backward cursor always pages backward. Pass the direction that matches the cursor to avoid confusion. - [JsonPropertyName("direction")] - public EventsReadDirection? Direction { get; set; } - - /// When false, skip ephemeral events entirely and return only durable (persisted) events. History-backfill callers that discard ephemerals anyway should set this so the read is bounded by the durable log length instead of racing the ephemeral ring on a busy session. Defaults to true (ephemerals are interleaved with durable events in creation order). Ignored by backward reads, which always cover persisted history only. - [JsonPropertyName("includeEphemeral")] - public bool? IncludeEphemeral { get; set; } - - /// Maximum number of events to return in this batch (1–1000, default 200). - [JsonPropertyName("max")] - public long? Max { get; set; } + /// Destination path using SessionFs conventions. + [JsonPropertyName("dest")] + public string Dest { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - /// Either '*' to receive all event types, or a non-empty list of event types to receive. - [JsonPropertyName("types")] - public JsonElement? Types { get; set; } - - /// Milliseconds to wait for new events when the cursor is at the tail of history. 0 (default) returns immediately even if no events are available. Capped at 30000ms. Ephemeral events that arrive during the wait are delivered in this batch but are NOT replayable on a subsequent read (use a non-zero waitMs in your next call to capture future ephemerals as they happen). This applies to forward reads only: a backward read always returns immediately and ignores `waitMs`, because backward paging covers persisted history only while new events append at the tail (the opposite end from a backward page), so no blocking or ephemeral delivery can occur. - [JsonConverter(typeof(MillisecondsTimeSpanConverter))] - [JsonPropertyName("waitMs")] - public TimeSpan? Wait { get; set; } + /// Source path using SessionFs conventions. + [JsonPropertyName("src")] + public string Src { get; set; } = string.Empty; } -/// 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). +/// Query results including rows, columns, and rows affected, or a filesystem error if execution failed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class EventLogTailResult +public sealed class SessionFsSqliteQueryResult { - /// Opaque cursor pointing at the current tail of the session's persisted-events history. Pass back to `read` to receive only events that arrive AFTER this snapshot. When the session has no events, this returns the same sentinel as an unset cursor (i.e. equivalent to omitting the cursor on a first read). - [JsonPropertyName("cursor")] - public string Cursor { get; set; } = string.Empty; + /// Column names from the result set. + [JsonPropertyName("columns")] + public IList Columns { get => field ??= []; set; } + + /// Describes a filesystem error. + [JsonPropertyName("error")] + public SessionFsError? Error { get; set; } + + /// SQLite last_insert_rowid() value for INSERT. + [JsonPropertyName("lastInsertRowid")] + public long? LastInsertRowid { get; set; } + + /// For SELECT: array of row objects. For others: empty array. + [JsonPropertyName("rows")] + public IList> Rows { get => field ??= []; set; } + + /// Number of rows affected (for INSERT/UPDATE/DELETE). + [JsonPropertyName("rowsAffected")] + public long RowsAffected { get; set; } } -/// Identifies the target session. +/// SQL query, query type, and optional bind parameters for executing a SQLite query against the per-session database. The provider applies its SQLite busy timeout for every call. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionEventLogTailRequest +public sealed class SessionFsSqliteQueryRequest { + /// Optional named bind parameters. + [JsonPropertyName("params")] + public IDictionary? Params { get; set; } + + /// SQL query to execute. + [JsonPropertyName("query")] + public string Query { get; set; } = string.Empty; + + /// How to execute the query: 'exec' for DDL/multi-statement (no results), 'query' for SELECT (returns rows), 'run' for INSERT/UPDATE/DELETE (returns rowsAffected). + [JsonPropertyName("queryType")] + public SessionFsSqliteQueryType QueryType { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Opaque handle representing an event-type interest registration. +/// Classified SQLite transaction failure. busyOrLocked guarantees rollback; postCommitAmbiguous must never be retried. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class RegisterEventInterestResult +public sealed class SessionFsSqliteTransactionError { - /// Opaque handle for this registration. Pass to releaseInterest to release. Each call to registerInterest produces a fresh handle, even when the same eventType is registered multiple times. - [JsonPropertyName("handle")] - public string Handle { get; set; } = string.Empty; + /// Machine-readable classification of the transaction failure. + [JsonPropertyName("errorClass")] + public SessionFsSqliteTransactionErrorClass ErrorClass { get; set; } + + /// Human-readable transaction failure message. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; } -/// Event type to register consumer interest for, used by runtime gating logic. +/// Per-statement results, or a classified transaction error. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class RegisterEventInterestParams +public sealed class SessionFsSqliteTransactionResult { - /// The event type the consumer wants the runtime to treat as 'observed' for behavior-switching gating. Some runtime code paths inspect whether any consumer is interested in a specific event type and choose a different implementation accordingly (e.g. `mcp.oauth_required`: when interest is registered the runtime delegates interactive OAuth token acquisition to the consumer via `mcp.oauth_required` events; when no interest is registered the runtime still attempts non-interactive reconnect from cached or refreshable tokens, and only marks the server `needs-auth` if usable credentials are unavailable — it does not open a browser or start interactive OAuth without a consumer). SDK clients that long-poll events do NOT automatically appear as listeners to these gating checks — they must explicitly call `registerInterest` for each event type they want the runtime to count as having a consumer. Multiple registrations for the same event type from the same or different consumers are tracked independently and must each be released. See: `mcp.oauth_required`, `sampling.requested`, `auto_mode_switch.requested`, `session_limits_exhausted.requested`, `user_input.requested`, `elicitation.requested`, `command.queued`, `exit_plan_mode.requested`. - [JsonPropertyName("eventType")] - public string EventType { get; set; } = string.Empty; + /// Classified transaction failure, when execution did not succeed. + [JsonPropertyName("error")] + public SessionFsSqliteTransactionError? Error { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Per-statement query results in input order. + [JsonPropertyName("results")] + public IList Results { get => field ??= []; set; } } -/// Indicates whether the operation succeeded. +/// One statement in an atomic SQLite transaction. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class EventLogReleaseInterestResult +public sealed class SessionFsSqliteTransactionStatement { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } -} + /// Optional named bind parameters. + [JsonPropertyName("params")] + public IDictionary? Params { get; set; } -/// Opaque handle previously returned by `registerInterest` to release. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ReleaseEventInterestParams -{ - /// Handle returned by a previous `registerInterest` call. Idempotent: releasing an unknown or already-released handle is a no-op (returns success). When the last outstanding handle for an event type is released, the runtime reverts to its 'no consumer' code path for that event type. - [JsonPropertyName("handle")] - public string Handle { get; set; } = string.Empty; + /// SQL statement to execute. + [JsonPropertyName("query")] + public string Query { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// How to execute the statement. + [JsonPropertyName("queryType")] + public SessionFsSqliteQueryType QueryType { get; set; } } -/// Identifies the target session. +/// Statements to execute atomically. Providers apply busy handling for every call. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionUsageGetMetricsRequest +public sealed class SessionFsSqliteTransactionRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Ordered SQL statements to execute in one transaction. + [JsonPropertyName("statements")] + public IList Statements { get => field ??= []; set; } } -/// Internal absolute code-change totals reported by the owning host. +/// Indicates whether the per-session SQLite database already exists. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionUsageSetCodeChangesRequest +public sealed class SessionFsSqliteExistsResult { - /// Absolute changed-file count; omission preserves the previous count. - [JsonPropertyName("filesCount")] - public double? FilesCount { get; set; } - - /// Absolute added-line total, replacing the previous reading. - [JsonPropertyName("linesAdded")] - public double LinesAdded { get; set; } - - /// Absolute removed-line total, replacing the previous reading. - [JsonPropertyName("linesRemoved")] - public double LinesRemoved { get; set; } + /// Whether the session database already exists. + [JsonPropertyName("exists")] + public bool Exists { get; set; } +} +/// Identifies the target session. +public sealed class SessionFsSqliteExistsRequest +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Existing delegation guidance for an account without delegation access. +/// Canvas open result returned by the provider. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionQuotaDelegateWarning +public sealed class CanvasProviderOpenResult { - /// User-facing guidance. - [JsonPropertyName("text")] - public string Text { get; set; } = string.Empty; + /// Provider-supplied status text. + [JsonPropertyName("status")] + public string? Status { get; set; } - /// Link to upgrade the account. + /// Provider-supplied title. + [JsonPropertyName("title")] + public string? Title { get; set; } + + /// URL for web-rendered canvases. [JsonPropertyName("url")] - public string Url { get; set; } = string.Empty; + public string? Url { get; set; } } -/// Session quota snapshot, preserving the incumbent numeric units and reset metadata. +/// Host capabilities. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionQuotaSnapshot +public sealed class CanvasHostContextCapabilities { - /// Entitled quantity in this quota's units. - [JsonPropertyName("entitlementRequests")] - public double EntitlementRequests { get; set; } - - /// Whether the provider reports available quota. - [JsonPropertyName("hasQuota")] - public bool? HasQuota { get; set; } - - /// Whether the entitlement is unlimited. - [JsonPropertyName("isUnlimitedEntitlement")] - public bool IsUnlimitedEntitlement { get; set; } - - /// Additional usage in this quota's units. - [JsonPropertyName("overage")] - public double Overage { get; set; } - - /// Whether additional usage is allowed after quota exhaustion. - [JsonPropertyName("overageAllowedWithExhaustedQuota")] - public bool OverageAllowedWithExhaustedQuota { get; set; } - - /// Additional-usage budget cap, when provided. - [JsonPropertyName("overageEntitlement")] - public double? OverageEntitlement { get; set; } - - /// Percentage of the entitlement remaining. - [JsonPropertyName("remainingPercentage")] - public double RemainingPercentage { get; set; } - - /// Quota reset time in milliseconds since the Unix epoch, when known. - [JsonPropertyName("resetDateEpochMs")] - public double? ResetDateEpochMs { get; set; } - - /// Whether the reset time is estimated. - [JsonPropertyName("resetDateEstimated")] - public bool? ResetDateEstimated { get; set; } - - /// Whether this quota uses token-based billing. - [JsonPropertyName("tokenBasedBilling")] - public bool? TokenBasedBilling { get; set; } - - /// Whether usage is allowed after quota exhaustion. - [JsonPropertyName("usageAllowedWithExhaustedQuota")] - public bool UsageAllowedWithExhaustedQuota { get; set; } - - /// Consumed quantity in this quota's units. - [JsonPropertyName("usedRequests")] - public double UsedRequests { get; set; } + /// Whether canvas rendering is supported. + [JsonPropertyName("canvases")] + public bool? Canvases { get; set; } } -/// The incumbent session-owned quota and account projection. +/// Host context supplied by the runtime. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionQuotaGetResult +public sealed class CanvasHostContext { - /// Whether the account can sign up for Copilot Free. - [JsonPropertyName("canSignupForCopilotFree")] - public bool CanSignupForCopilotFree { get; set; } - - /// Whether the account can delegate tasks to GitHub. - [JsonPropertyName("delegateAvailable")] - public bool DelegateAvailable { get; set; } - - /// Existing delegation warning for a free account. - [JsonPropertyName("delegateWarning")] - public SessionQuotaDelegateWarning? DelegateWarning { get; set; } - - /// Whether dynamic workflows are enabled for the active account. - [JsonPropertyName("dynamicWorkflowsEnabled")] - public bool DynamicWorkflowsEnabled { get; set; } - - /// Whether dynamic workflows are visible under the session's feature flags. - [JsonPropertyName("dynamicWorkflowsUiVisible")] - public bool DynamicWorkflowsUiVisible { get; set; } - - /// Whether the account uses the free limited Copilot plan. - [JsonPropertyName("isFreeUser")] - public bool IsFreeUser { get; set; } + /// Host capabilities. + [JsonPropertyName("capabilities")] + public CanvasHostContextCapabilities? Capabilities { get; set; } +} - /// Whether the account uses token-based billing. - [JsonPropertyName("isTbbUser")] - public bool IsTbbUser { get; set; } +/// Session context supplied by the runtime. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class CanvasSessionContext +{ + /// Active session working directory, when known. + [JsonPropertyName("workingDirectory")] + public string? WorkingDirectory { get; set; } +} - /// Whether model cost columns should be shown. - [JsonPropertyName("modelCostColumnVisible")] - public bool ModelCostColumnVisible { get; set; } +/// Canvas open parameters sent to the provider. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class CanvasProviderOpenRequest +{ + /// Provider-local canvas identifier. + [JsonPropertyName("canvasId")] + public string CanvasId { get; set; } = string.Empty; - /// Resolved Copilot plan tier. - [JsonPropertyName("planTier")] - public SessionQuotaPlanTier PlanTier { get; set; } + /// Owning provider identifier. + [JsonPropertyName("extensionId")] + public string ExtensionId { get; set; } = string.Empty; - /// Whether premium-request costs are billable. - [JsonPropertyName("premiumRequestsBillable")] - public bool PremiumRequestsBillable { get; set; } + /// Host context supplied by the runtime. + [JsonPropertyName("host")] + public CanvasHostContext? Host { get; set; } - /// Provider-owned account quota state, including snapshots without numeric balances. - [JsonPropertyName("providerQuotas")] - public IList? ProviderQuotas { get; set; } + /// Canvas open input. + [JsonPropertyName("input")] + public JsonElement? Input { get; set; } - /// Known quota snapshots, keyed by the provider's quota type. - [JsonPropertyName("snapshots")] - public IDictionary Snapshots { get => field ??= new Dictionary(); set; } + /// Stable caller-supplied canvas instance identifier. + [JsonPropertyName("instanceId")] + public string InstanceId { get; set; } = string.Empty; - /// Upgrade link for a free account. - [JsonPropertyName("upgradeUrl")] - public string? UpgradeUrl { get; set; } -} + /// Session context supplied by the runtime. + [JsonPropertyName("session")] + public CanvasSessionContext? Session { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionQuotaGetRequest -{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// The incumbent session-owned quota and account projection. +/// Canvas close parameters sent to the provider. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionQuotaRefreshResult +public sealed class CanvasProviderCloseRequest { - /// Whether the account can sign up for Copilot Free. - [JsonPropertyName("canSignupForCopilotFree")] - public bool CanSignupForCopilotFree { get; set; } + /// Provider-local canvas identifier. + [JsonPropertyName("canvasId")] + public string CanvasId { get; set; } = string.Empty; - /// Whether the account can delegate tasks to GitHub. - [JsonPropertyName("delegateAvailable")] - public bool DelegateAvailable { get; set; } + /// Owning provider identifier. + [JsonPropertyName("extensionId")] + public string ExtensionId { get; set; } = string.Empty; - /// Existing delegation warning for a free account. - [JsonPropertyName("delegateWarning")] - public SessionQuotaDelegateWarning? DelegateWarning { get; set; } + /// Host context supplied by the runtime. + [JsonPropertyName("host")] + public CanvasHostContext? Host { get; set; } - /// Whether dynamic workflows are enabled for the active account. - [JsonPropertyName("dynamicWorkflowsEnabled")] - public bool DynamicWorkflowsEnabled { get; set; } + /// Canvas instance identifier. + [JsonPropertyName("instanceId")] + public string InstanceId { get; set; } = string.Empty; - /// Whether dynamic workflows are visible under the session's feature flags. - [JsonPropertyName("dynamicWorkflowsUiVisible")] - public bool DynamicWorkflowsUiVisible { get; set; } + /// Session context supplied by the runtime. + [JsonPropertyName("session")] + public CanvasSessionContext? Session { get; set; } - /// Whether the account uses the free limited Copilot plan. - [JsonPropertyName("isFreeUser")] - public bool IsFreeUser { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Whether the account uses token-based billing. - [JsonPropertyName("isTbbUser")] - public bool IsTbbUser { get; set; } +/// Canvas action invocation parameters sent to the provider. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class CanvasProviderInvokeActionRequest +{ + /// Action name to invoke. + [JsonPropertyName("actionName")] + public string ActionName { get; set; } = string.Empty; - /// Whether model cost columns should be shown. - [JsonPropertyName("modelCostColumnVisible")] - public bool ModelCostColumnVisible { get; set; } + /// Provider-local canvas identifier. + [JsonPropertyName("canvasId")] + public string CanvasId { get; set; } = string.Empty; - /// Resolved Copilot plan tier. - [JsonPropertyName("planTier")] - public SessionQuotaPlanTier PlanTier { get; set; } + /// Owning provider identifier. + [JsonPropertyName("extensionId")] + public string ExtensionId { get; set; } = string.Empty; - /// Whether premium-request costs are billable. - [JsonPropertyName("premiumRequestsBillable")] - public bool PremiumRequestsBillable { get; set; } + /// Host context supplied by the runtime. + [JsonPropertyName("host")] + public CanvasHostContext? Host { get; set; } - /// Provider-owned account quota state, including snapshots without numeric balances. - [JsonPropertyName("providerQuotas")] - public IList? ProviderQuotas { get; set; } + /// Action input. + [JsonPropertyName("input")] + public JsonElement? Input { get; set; } - /// Known quota snapshots, keyed by the provider's quota type. - [JsonPropertyName("snapshots")] - public IDictionary Snapshots { get => field ??= new Dictionary(); set; } + /// Canvas instance identifier. + [JsonPropertyName("instanceId")] + public string InstanceId { get; set; } = string.Empty; - /// Upgrade link for a free account. - [JsonPropertyName("upgradeUrl")] - public string? UpgradeUrl { get; set; } -} + /// Session context supplied by the runtime. + [JsonPropertyName("session")] + public CanvasSessionContext? Session { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionQuotaRefreshRequest -{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// A pending session quota warning, consumed once by takeWarnings. +/// Reports a supervised listener's hosting-task termination and cleanup outcome. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QuotaWarningProjection +public sealed class HostExitedRequest { - /// User-facing warning text. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; + /// Explicit startup or teardown failure, when present. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Related account or upgrade link. - [JsonPropertyName("url")] - public string? Url { get; set; } + /// Process exit status when available; absent for in-process listener tasks. + [JsonPropertyName("exitCode")] + public long? ExitCode { get; set; } - /// Existing warning category. - [JsonPropertyName("warningType")] - public string WarningType { get; set; } = string.Empty; + /// Listener UUID. + [JsonPropertyName("hostId")] + public string HostId { get; set; } = string.Empty; + + /// Cause of termination. + [JsonPropertyName("reason")] + public HostExitReason Reason { get; set; } } -/// Identifies the target session. +/// Opaque integrator-owned process launch profile for one extension entrypoint. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionQuotaTakeWarningsRequest +public sealed class ExtensionLaunchProfile { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Opaque integrator-defined arguments passed to the executable. The runtime does not append the extension entrypoint. + [JsonPropertyName("args")] + public IList Args { get => field ??= []; set; } + + /// Opaque integrator-defined environment variables. Runtime-owned COPILOT_SDK_PATH, SESSION_ID, and COPILOT_EXTENSION_PARENT_PID values take precedence. + [JsonPropertyName("env")] + public IDictionary Env { get => field ??= new Dictionary(); set; } + + /// Executable used to launch the extension entrypoint. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("executable")] + public string Executable { get; set; } = string.Empty; } -/// Prediction result. Available results include prediction details; unavailable results include an explicit reason. -/// Polymorphic base type discriminated by kind. +/// The launch profile for a supported entrypoint. Omit launch when the provider does not support the entrypoint. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(SessionLimitPredictionResultAvailable), "available")] -[JsonDerivedType(typeof(SessionLimitPredictionResultUnavailable), "unavailable")] -public partial class SessionLimitPredictionResult +public sealed class ExtensionLaunchProviderResolveResult { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; + /// Opaque launch profile, omitted when this provider does not support the entrypoint. + [JsonPropertyName("launch")] + public ExtensionLaunchProfile? Launch { get; set; } } - -/// Baseline data provenance for a prediction. +/// A discovered extension entrypoint that the registered integrator may classify and resolve to an opaque launch profile. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionLimitPredictionBaselineData +public sealed class ExtensionLaunchProviderResolveRequest { - /// End of the baseline data slice. - [JsonPropertyName("windowEnd")] - public string WindowEnd { get; set; } = string.Empty; + /// Source-qualified extension identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// Start of the baseline data slice. - [JsonPropertyName("windowStart")] - public string WindowStart { get; set; } = string.Empty; + /// Absolute path to the discovered extension entrypoint. + [JsonPropertyName("modulePath")] + public string ModulePath { get; set; } = string.Empty; + + /// Human-readable extension name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Discovery source for the extension entrypoint. + [JsonPropertyName("source")] + public ExtensionSource Source { get; set; } } -/// Semantic usage tier and its AI-credit cap. +/// Acknowledgement. Returning successfully simply means the SDK accepted the start frame; it does not imply the request will succeed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionLimitPredictionTierOption +public sealed class LlmInferenceHttpRequestStartResult { - /// AI-credit cap for this tier. - [JsonPropertyName("cap")] - public double Cap { get; set; } - - /// Semantic usage tier. - [JsonPropertyName("tier")] - public SessionLimitPredictionTier Tier { get; set; } } -/// Explainable AI-credit session-limit prediction. +/// The head of an outbound model-layer HTTP request. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionLimitPredictionDetails +public sealed class LlmInferenceHttpRequestStartRequest { - /// Baseline data provenance. - [JsonPropertyName("baselineData")] - public SessionLimitPredictionBaselineData BaselineData { get => field ??= new(); set; } + /// Stable identity of the agent trajectory that issued this request. Present when the request originates from an agent turn; absent for requests outside any agent context. This is the same identity used by lifecycle and bridged session events and remains constant across turns and retries. + [JsonPropertyName("agentId")] + public string? AgentId { get; set; } - /// Client population used for the prediction. - [JsonPropertyName("clientType")] - public SessionLimitPredictionClientType ClientType { get; set; } + /// Identity of the agent invocation (one agentic loop) that issued this request. It remains fixed across physical retries within the invocation and is distinct from the stable trajectory `agentId`. A caller-supplied invocation id always takes precedence (this covers auxiliary calls that have no model call id). Otherwise, first-party CAPI requests fall back to the runtime's agent task id — the same value the runtime emits as the `X-Agent-Task-Id` header — while custom-provider requests fall back to the model call id. + [JsonPropertyName("agentInvocationId")] + public string? AgentInvocationId { get; set; } - /// Resolved model family when known. - [JsonPropertyName("family")] - public string? Family { get; set; } + /// HTTP request headers, preserving multiple values per name. + [JsonPropertyName("headers")] + public IDictionary> Headers { get => field ??= new Dictionary>(); set; } - /// Model identifier used for lookup. - [JsonPropertyName("modelId")] - public string ModelId { get; set; } = string.Empty; + /// Coarse classification of the interaction that produced this request. Open string for forward-compatibility; known values include `conversation-agent`, `conversation-subagent`, `conversation-sampling`, `conversation-background`, `conversation-compaction`, and `conversation-user`. Absent when the runtime did not classify the request. Comes from the runtime's per-request agent context independently of transport; on the CAPI transport the runtime derives the upstream `X-Interaction-Type` header from this same context. + [JsonPropertyName("interactionType")] + public string? InteractionType { get; set; } - /// Recommended maximum AI credits for this session. - [JsonPropertyName("recommendedCap")] - public double RecommendedCap { get; set; } + /// HTTP method, e.g. GET, POST. + [JsonPropertyName("method")] + public string Method { get; set; } = string.Empty; - /// Tier chosen as the recommended cap. - [JsonPropertyName("recommendedTier")] - public SessionLimitPredictionTier RecommendedTier { get; set; } + /// Stable identity of the immediate parent trajectory. Present for child trajectories such as subagents and conversation-sampling requests; absent for root-agent and non-agent requests. + [JsonPropertyName("parentAgentId")] + public string? ParentAgentId { get; set; } - /// Baseline fallback level used to create the prediction. - [JsonPropertyName("source")] - public SessionLimitPredictionSource Source { get; set; } + /// Opaque runtime-minted id, unique per in-flight request. The SDK uses this to correlate httpRequestChunk frames and to address its httpResponseStart / httpResponseChunk replies back to the runtime. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; - /// Key matched at the source level, such as a model id, family id, or `global`. - [JsonPropertyName("sourceKey")] - public string SourceKey { get; set; } = string.Empty; + /// Id of the runtime session that triggered this request, when one is in scope. Absent for requests issued outside any session (e.g. startup model-catalog or capability resolution). This is a payload field — not a dispatch key — because the client-global API is registered process-wide rather than per session. + [JsonPropertyName("sessionId")] + public string? SessionId { get; set; } - /// Ordered usage tiers and their AI-credit caps. - [JsonPropertyName("tiers")] - public IList Tiers { get => field ??= []; set; } + /// Transport the runtime would otherwise use for this request. `http` (the default when absent) covers plain HTTP and SSE responses; `websocket` indicates a full-duplex message channel where each body chunk maps to one WebSocket message and the `binary` flag distinguishes text from binary frames. The SDK consumer uses this to decide whether to service the request with an HTTP client or a WebSocket client. It is the one piece of request metadata the consumer cannot reliably infer from the URL or headers alone. + [JsonPropertyName("transport")] + public LlmInferenceHttpRequestStartTransport? Transport { get; set; } + + /// Absolute request URL. + [JsonPropertyName("url")] + public string Url { get; set; } = string.Empty; } -/// The available variant of . +/// Acknowledgement. The SDK is free to ignore the ack and treat chunk delivery as fire-and-forget. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SessionLimitPredictionResultAvailable : SessionLimitPredictionResult +public sealed class LlmInferenceHttpRequestChunkResult { - /// - [JsonIgnore] - public override string Kind => "available"; - - /// Predicted session limit details. - [JsonPropertyName("prediction")] - public required SessionLimitPredictionDetails Prediction { get; set; } } -/// The unavailable variant of . +/// A request body chunk or cancellation signal. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SessionLimitPredictionResultUnavailable : SessionLimitPredictionResult +public sealed class LlmInferenceHttpRequestChunkRequest { - /// - [JsonIgnore] - public override string Kind => "unavailable"; + /// Identity of the agent invocation (one agentic loop) this body chunk belongs to, matching the `agentInvocationId` semantics on httpRequestStart. Carried per chunk so a persistent transport can attribute successive turns correctly: when a WebSocket connection is reused across turns, the httpRequestStart identity reflects only the turn that opened the connection, so each later turn stamps its own invocation id here. Absent when the runtime has no invocation context for the request, or on the plain-HTTP transport where every request has its own httpRequestStart. + [JsonPropertyName("agentInvocationId")] + public string? AgentInvocationId { get; set; } - /// Reason no prediction is available. - [JsonPropertyName("reason")] - public required SessionLimitPredictionUnavailableReason Reason { get; set; } -} + /// When true, `data` is base64-encoded bytes. When absent or false, `data` is UTF-8 text. + [JsonPropertyName("binary")] + public bool? Binary { get; set; } -/// RPC data type for SessionLimitPredictionPredict operations. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionLimitPredictionPredictRequest -{ - /// Client type to size for. Defaults to `cli-interactive`. - [JsonPropertyName("clientType")] - public SessionLimitPredictionClientType? ClientType { get; set; } + /// When true, the runtime is cancelling the in-flight request (e.g. upstream consumer aborted). `data` is ignored. Implies end-of-request. + [JsonPropertyName("cancel")] + public bool? Cancel { get; set; } - /// Optional model identifier override. If omitted, the session's current model is used. - [JsonPropertyName("modelId")] - public string? ModelId { get; set; } -} + /// Optional human-readable reason for the cancellation, propagated for logging. + [JsonPropertyName("cancelReason")] + public string? CancelReason { get; set; } -/// RPC data type for SessionLimitPredictionPredictRequestWithSession operations. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionLimitPredictionPredictRequestWithSession -{ - /// Client type to size for. Defaults to `cli-interactive`. - [JsonPropertyName("clientType")] - public SessionLimitPredictionClientType? ClientType { get; set; } + /// Body byte range. UTF-8 text when `binary` is absent or false; base64-encoded bytes when `binary` is true. May be empty. + [JsonPropertyName("data")] + public string Data { get; set; } = string.Empty; - /// Optional model identifier override. If omitted, the session's current model is used. - [JsonPropertyName("modelId")] - public string? ModelId { get; set; } + /// When true, this is the final body chunk for the request. The SDK may rely on having received an end-marked chunk before treating the request body as complete. + [JsonPropertyName("end")] + public bool? End { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Matches the requestId from the originating httpRequestStart frame. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; } -/// GitHub URL for the session and a flag indicating whether remote steering is enabled. +/// Client environment metadata describing the process that produced a telemetry event. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class RemoteEnableResult +public sealed class GitHubTelemetryClientInfo { - /// Whether remote steering is enabled. - [JsonPropertyName("remoteSteerable")] - public bool RemoteSteerable { get; set; } + /// Copilot CLI version string. + [JsonPropertyName("cli_version")] + public string CliVersion { get; set; } = string.Empty; - /// GitHub frontend URL for this session. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("url")] - public string? Url { get; set; } -} + /// Name of the client application. + [JsonPropertyName("client_name")] + public string? ClientName { get; set; } -/// Optional remote session mode ("off", "export", or "on"); defaults to enabling both export and remote steering. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class RemoteEnableRequest -{ - /// Per-session remote mode. "off" disables remote, "export" exports session events to GitHub without enabling remote steering, "on" enables both export and remote steering. - [JsonPropertyName("mode")] - public RemoteSessionMode? Mode { get; set; } + /// Type of client. + [JsonPropertyName("client_type")] + public string? ClientType { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Copilot subscription plan, when known. + [JsonPropertyName("copilot_plan")] + public string? CopilotPlan { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionRemoteDisableRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Number of logical CPU cores on the host. + [JsonPropertyName("cpu_count")] + public long? CpuCount { get; set; } -/// 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. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class RemoteNotifySteerableChangedResult -{ -} + /// Distinct CPU model names for the host, comma-separated. + [JsonPropertyName("cpu_model")] + public string? CpuModel { get; set; } -/// New remote-steerability state to persist as a `session.remote_steerable_changed` event. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class RemoteNotifySteerableChangedRequest -{ - /// Whether the session now supports remote steering via GitHub. The runtime persists this as a `session.remote_steerable_changed` event so resume/replay sees the up-to-date capability. - [JsonPropertyName("remoteSteerable")] - public bool RemoteSteerable { get; set; } + /// Stable machine identifier for the device. + [JsonPropertyName("dev_device_id")] + public string? DevDeviceId { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Whether the user is a GitHub/Microsoft staff member. + [JsonPropertyName("is_staff")] + public bool? IsStaff { get; set; } -/// Current sharing status and shareable GitHub URL for a session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class VisibilityGetResult -{ - /// Shareable GitHub URL for the session. Present when the session is synced and the URL can be resolved. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("shareUrl")] - public string? ShareUrl { get; set; } + /// Node.js runtime version string. + [JsonPropertyName("node_version")] + public string NodeVersion { get; set; } = string.Empty; - /// Current sharing status. Absent when the session is not synced or the status could not be retrieved (e.g. the user is not authenticated). - [JsonPropertyName("status")] - public SessionVisibilityStatus? Status { get; set; } + /// Operating system architecture (e.g. arm64, x64). + [JsonPropertyName("os_arch")] + public string OsArch { get; set; } = string.Empty; - /// Whether the session has been synced to Mission Control (i.e. has a GitHub task). When false, the session cannot be shared and `status`/`shareUrl` are absent. - [JsonPropertyName("synced")] - public bool Synced { get; set; } -} + /// Operating system platform (e.g. darwin, linux, win32). + [JsonPropertyName("os_platform")] + public string OsPlatform { get; set; } = string.Empty; -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionVisibilityGetRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Operating system version string. + [JsonPropertyName("os_version")] + public string OsVersion { get; set; } = string.Empty; } -/// Effective sharing status and shareable GitHub URL after updating session visibility. +/// A single telemetry event in the runtime's native GitHub-shaped telemetry format, forwarded verbatim to opted-in hosts. The `restricted` flag on the enclosing GitHubTelemetryNotification distinguishes standard from restricted events; the payload shape is identical for both. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class VisibilitySetResult +public sealed class GitHubTelemetryEvent { - /// Shareable GitHub URL for the session. Present when the session is synced and the URL can be resolved. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("shareUrl")] - public string? ShareUrl { get; set; } + /// Client environment metadata. + [JsonPropertyName("client")] + public GitHubTelemetryClientInfo? Client { get; set; } - /// Effective sharing status after the update. May differ from the requested status for task types that are already visible to repository readers by default. Absent when the update could not be applied (e.g. the session is not synced or the user is not authenticated). - [JsonPropertyName("status")] - public SessionVisibilityStatus? Status { get; set; } + /// Copilot tracking ID for user-level attribution. + [JsonPropertyName("copilot_tracking_id")] + public string? CopilotTrackingId { get; set; } - /// Whether the session has been synced to Mission Control (i.e. has a GitHub task). When false, the visibility change could not be applied and `status`/`shareUrl` are absent. - [JsonPropertyName("synced")] - public bool Synced { get; set; } -} + /// Timestamp when the event was created (ISO 8601 format). + [JsonPropertyName("created_at")] + public string? CreatedAt { get; set; } -/// Desired sharing status for the session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class VisibilitySetRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Experiment assignment context. + [JsonPropertyName("exp_assignment_context")] + public string? ExpAssignmentContext { get; set; } - /// Sharing status to apply. "repo" makes the session visible to repository readers; "unshared" restricts it to the creator and collaborators. - [JsonPropertyName("status")] - public SessionVisibilityStatus Status { get; set; } + /// Feature flags enabled for this session, as a map from flag to value. + [JsonPropertyName("features")] + public IDictionary? Features { get; set; } + + /// Event type/kind (e.g. get_completion_with_tools_turn, tool_call_executed). + [JsonPropertyName("kind")] + public string Kind { get; set; } = string.Empty; + + /// Numeric metrics as a map from key to value. + [JsonPropertyName("metrics")] + public IDictionary Metrics { get => field ??= new Dictionary(); set; } + + /// Reference to the model call that produced this event. + [JsonPropertyName("model_call_id")] + public string? ModelCallId { get; set; } + + /// String-valued properties as a map from key to value. + [JsonPropertyName("properties")] + public IDictionary Properties { get => field ??= new Dictionary(); set; } + + /// Session identifier the event belongs to. + [JsonPropertyName("session_id")] + public string? SessionId { get; set; } } -/// Scheduled prompt entry with ID, timing (`intervalMs`, `cron`, or `at`), prompt text, recurrence, and next run time. +/// Payload for a `gitHubTelemetry.event` notification: a single GitHub telemetry event the runtime forwards to a host connection that opted into telemetry forwarding during the `server.connect` handshake. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ScheduleEntry +public sealed class GitHubTelemetryNotification { - /// Absolute fire time (epoch milliseconds) for a one-shot calendar schedule. - [JsonPropertyName("at")] - public long? At { get; set; } - - /// 5-field cron expression for a recurring calendar schedule, evaluated in `tz`. - [JsonPropertyName("cron")] - public string? Cron { get; set; } + /// The telemetry event, in the runtime's native GitHub-shaped telemetry format. + [JsonPropertyName("event")] + public GitHubTelemetryEvent Event { get => field ??= new(); set; } - /// Display-only label for the prompt as shown in the UI (e.g. `/skill-name` for a skill-invocation schedule). The actual enqueued prompt is `prompt`. - [JsonPropertyName("displayPrompt")] - public string? DisplayPrompt { get; set; } + /// Whether this is a restricted telemetry event (cli.restricted_telemetry). Hosts must route restricted events to first-party Microsoft stores only. + [JsonPropertyName("restricted")] + public bool Restricted { get; set; } - /// Sequential id assigned by the runtime within the session. Stable across resumes (rebuilt from the event log). - [JsonPropertyName("id")] - public long Id { get; set; } + /// Session the telemetry event belongs to, when it is session-scoped. Omitted for sessionless events (for example, `server.sendTelemetry` calls with no session id), which are still forwarded to opted-in connections. + [JsonPropertyName("sessionId")] + public string? SessionId { get; set; } +} - /// Interval between scheduled ticks, in milliseconds (relative-interval schedules). - [JsonConverter(typeof(MillisecondsTimeSpanConverter))] - [JsonPropertyName("intervalMs")] - public TimeSpan? Interval { get; set; } +/// SDK host response to a GitHub credential request. +/// Polymorphic base type discriminated by kind. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(GitHubTokenAcquireResultToken), "token")] +[JsonDerivedType(typeof(GitHubTokenAcquireResultCancelled), "cancelled")] +public partial class GitHubTokenAcquireResult +{ + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// ISO 8601 timestamp when the next tick is scheduled to fire. - [JsonPropertyName("nextRunAt")] - public DateTimeOffset NextRunAt { get; set; } - /// Prompt text that gets enqueued on every tick. - [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; +/// The token variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class GitHubTokenAcquireResultToken : GitHubTokenAcquireResult +{ + /// + [JsonIgnore] + public override string Kind => "token"; - /// Whether the schedule re-arms after each tick (`/every`) or fires once (`/after`). - [JsonPropertyName("recurring")] - public bool Recurring { get; set; } + /// GitHub access token acquired by the SDK host. + [JsonPropertyName("accessToken")] + public required string AccessToken { get; set; } - /// True for a self-paced (`dynamic`) schedule: no fixed cadence; the model arms each next run via the `manage_schedule` `wakeup` action. `nextRunAt` is model-controlled. - [JsonPropertyName("selfPaced")] - public bool? SelfPaced { get; set; } + /// Remaining token lifetime in seconds when callback execution completes. It must exceed the one-hour preflight refresh threshold. + [JsonPropertyName("expiresIn")] + public required long ExpiresIn { get; set; } - /// IANA timezone the `cron` expression is evaluated in. - [JsonPropertyName("tz")] - public string? Tz { get; set; } + /// OAuth token type. Defaults to bearer when omitted. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("tokenType")] + public string? TokenType { get; set; } } -/// Snapshot of the currently active recurring prompts for this session. +/// The cancelled variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ScheduleList +public partial class GitHubTokenAcquireResultCancelled : GitHubTokenAcquireResult { - /// Active scheduled prompts, ordered by id. - [JsonPropertyName("entries")] - public IList Entries { get => field ??= []; set; } + /// + [JsonIgnore] + public override string Kind => "cancelled"; } -/// Identifies the target session. +/// Asks the SDK client to acquire a GitHub access token from an opaque callback registration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionScheduleListRequest +public sealed class GitHubTokenAcquireRequest { - /// Target session identifier. + /// Effective GitHub host for which the callback must return a token. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; + + /// Why the runtime is requesting a GitHub credential. + [JsonPropertyName("reason")] + public GitHubTokenAcquireReason Reason { get; set; } + + /// Opaque identifier generated by the SDK for this callback registration. + [JsonPropertyName("registrationId")] + public string RegistrationId { get; set; } = string.Empty; + + /// Session receiving the token. Absent only before a cloud session has been assigned its id. [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + public string? SessionId { get; set; } } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionScheduleHydrateRequest +/// A response is meaningful only on the connection and request that issued its challenge. +public sealed class InstallationsConfirmResult { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Exact challenge from the request. + [JsonPropertyName("confirmationId")] + public string ConfirmationId { get; set; } = string.Empty; + + /// Fresh explicit user decision. There is no default. + [JsonPropertyName("decision")] + public InstallationDecision Decision { get; set; } + + /// Exact review commitment from the request. + [JsonPropertyName("reviewFingerprint")] + public string ReviewFingerprint { get; set; } = string.Empty; } -/// Whether the session currently has an active self-paced schedule. +/// Only resource kinds with an implemented installation engine have a review variant. +/// Polymorphic base type discriminated by resource. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ScheduleHasSelfPacedResult +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "resource", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(InstallationReviewMcp), "mcp")] +[JsonDerivedType(typeof(InstallationReviewSkill), "skill")] +public partial class InstallationReview { - /// True when at least one active schedule is self-paced. - [JsonPropertyName("hasSelfPaced")] - public bool HasSelfPaced { get; set; } + /// The type discriminator. + [JsonPropertyName("resource")] + public virtual string Resource { get; set; } = string.Empty; } -/// Identifies the target session. + +/// Safe MCP review fields. No raw card, retrieval URL, plan handle or secret value. +/// Polymorphic base type discriminated by action. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionScheduleHasSelfPacedRequest +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "action", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(McpInstallationReviewInstall), "install")] +[JsonDerivedType(typeof(McpInstallationReviewUninstall), "uninstall")] +public partial class McpInstallationReview { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// The type discriminator. + [JsonPropertyName("action")] + public virtual string Action { get; set; } = string.Empty; } -/// Result of registering or re-arming a scheduled prompt. + +/// +/// Final remote configuration, not a template. The producer refuses external-value +/// expansion before presenting this review. Receipt-owned secrets appear only as +/// `${installation-secret:<id>}` references whose `<id>` matches a reviewed +/// `${secret:<id>}` placeholder; values are never included. +/// [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ScheduleAddResult +public sealed class McpInstallationRemoteConfiguration { - /// The registered or updated schedule entry. - [JsonPropertyName("entry")] - public ScheduleEntry? Entry { get; set; } + /// + /// Configured headers, excluding separately authorised OAuth tokens. Values may + /// contain owned secret references, never secret values. + /// + [JsonPropertyName("headers")] + public IDictionary Headers { get => field ??= new Dictionary(); set; } - /// User-facing validation error, when registration failed. - [JsonPropertyName("error")] - public string? Error { get; set; } + /// Configured tool selection, not permission to invoke those tools. + [JsonPropertyName("tools")] + public IList Tools { get => field ??= []; set; } + + /// Transport in the effective persisted remote configuration. + [JsonPropertyName("transport")] + public McpPlanRemoteTransport Transport { get; set; } + + /// Exact resolved endpoint, without templates or secret placeholders. + [JsonPropertyName("url")] + public string Url { get; set; } = string.Empty; } -/// Register a relative-interval scheduled prompt. +/// The install variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ScheduleAddRequest +public partial class McpInstallationReviewInstall : McpInstallationReview { - /// Optional display-only prompt label. - [JsonPropertyName("displayPrompt")] - public string? DisplayPrompt { get; set; } + /// + [JsonIgnore] + public override string Action => "install"; - /// Human-readable interval such as `30s`, `5m`, or `2h`. - [JsonPropertyName("interval")] - public string Interval { get; set; } = string.Empty; + /// Catalogue identity retained from the bound candidate when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("catalogue")] + public InstallationCatalogueIdentity? Catalogue { get; set; } - /// Prompt text to enqueue when the schedule fires. - [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; + /// Original catalogue trust metadata, not a verification claim. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("catalogueTrust")] + public CatalogTrustSnapshot? CatalogueTrust { get; set; } - /// Whether the schedule should re-arm after each tick. Defaults to true. - [JsonPropertyName("recurring")] - public bool? Recurring { get; set; } + /// The configuration change for the selected alternative only. + [JsonPropertyName("configurationChange")] + public required McpPlanConfigurationChange ConfigurationChange { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// + /// Complete effective remote configuration for final installation review. + /// Earlier private selection reviews and package choices omit this field. + /// The owned remote resource requires it before issuing confirmation. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("effectiveConfiguration")] + public McpInstallationRemoteConfiguration? EffectiveConfiguration { get; set; } -/// Register a cron scheduled prompt. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ScheduleAddCronRequest -{ - /// 5-field cron expression. - [JsonPropertyName("cron")] - public string Cron { get; set; } = string.Empty; + /// Identity from the retained plan, not caller display text. + [JsonPropertyName("identity")] + public required McpPlanResourceIdentity Identity { get; set; } - /// Optional display-only prompt label. - [JsonPropertyName("displayPrompt")] - public string? DisplayPrompt { get; set; } + /// Non-secret values supplied for this selected alternative. + [JsonPropertyName("inputs")] + public required IList Inputs { get; set; } - /// Prompt text to enqueue when the schedule fires. - [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; + /// Policy decision bound to this plan. + [JsonPropertyName("policy")] + public required McpPlanPolicyResult Policy { get; set; } - /// Whether the schedule should re-arm after each tick. Defaults to true. - [JsonPropertyName("recurring")] - public bool? Recurring { get; set; } + /// Original source identity and content commitment. + [JsonPropertyName("provenance")] + public required McpPlanProvenance Provenance { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Explicit reviewed backend selection; no backend is accessed when no secrets are supplied. + [JsonPropertyName("secretStorage")] + public required McpInstallationSecretStorage SecretStorage { get; set; } - /// IANA timezone for evaluating the cron expression. - [JsonPropertyName("tz")] - public string? Tz { get; set; } + /// Only the selected alternative is applied. + [JsonPropertyName("selectedChoice")] + public required McpPlanTransportChoice SelectedChoice { get; set; } + + /// Exact reviewed placeholders supplied separately. Never secret values. + [JsonPropertyName("suppliedSecrets")] + public required IList SuppliedSecrets { get; set; } + + /// Exact reviewed user-scope destination. + [JsonPropertyName("target")] + public required McpPlanTarget Target { get; set; } } -/// Register an absolute-time scheduled prompt. +/// The uninstall variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ScheduleAddAtRequest +public partial class McpInstallationReviewUninstall : McpInstallationReview { - /// Epoch milliseconds when the prompt should fire. - [JsonPropertyName("at")] - public long At { get; set; } + /// + [JsonIgnore] + public override string Action => "uninstall"; - /// Optional display-only prompt label. - [JsonPropertyName("displayPrompt")] - public string? DisplayPrompt { get; set; } + /// Identity from the installed receipt. + [JsonPropertyName("identity")] + public required McpPlanResourceIdentity Identity { get; set; } - /// Prompt text to enqueue when the schedule fires. - [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; + /// Receipt-owned installation being removed. + [JsonPropertyName("installationId")] + public required string InstallationId { get; set; } - /// Whether the schedule should re-arm after each tick. Defaults to false. - [JsonPropertyName("recurring")] - public bool? Recurring { get; set; } + /// Exact planner-owned secret slots to remove, excluding shared OAuth grants. + [JsonPropertyName("ownedSecretCount")] + public required long OwnedSecretCount { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Current removal policy, independent of permission to activate the server. + [JsonPropertyName("policy")] + public required McpPlanPolicyResult Policy { get; set; } -/// Register a self-paced scheduled prompt. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ScheduleAddSelfPacedRequest -{ - /// Optional display-only prompt label. - [JsonPropertyName("displayPrompt")] - public string? DisplayPrompt { get; set; } + /// Shared profile authentication is deliberately retained, not pending cleanup. + [JsonPropertyName("preservesSharedAuthentication")] + public required bool PreservesSharedAuthentication { get; set; } - /// Prompt text to enqueue when the schedule fires. - [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; + /// Source identity and content commitment retained by the installed receipt. + [JsonPropertyName("provenance")] + public required McpPlanProvenance Provenance { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether uninstall restores a protected pre-install configuration. + [JsonPropertyName("restoresPreviousConfiguration")] + public required bool RestoresPreviousConfiguration { get; set; } + + /// Exact destination, checked for intervening changes before mutation. + [JsonPropertyName("target")] + public required McpPlanTarget Target { get; set; } } -/// Re-arm a self-paced scheduled prompt. +/// The mcp variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ScheduleRearmSelfPacedRequest +public partial class InstallationReviewMcp : InstallationReview { - /// Epoch milliseconds when the prompt should next fire. - [JsonPropertyName("at")] - public long At { get; set; } - - /// Id of the self-paced scheduled prompt. - [JsonPropertyName("id")] - public long Id { get; set; } + /// + [JsonIgnore] + public override string Resource => "mcp"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// The exact MCP action and its reviewed changes. + [JsonPropertyName("review")] + public required McpInstallationReview Review { get; set; } } -/// Remove a scheduled prompt by id. The result entry is omitted if the id was unknown. +/// The skill variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ScheduleStopResult +public partial class InstallationReviewSkill : InstallationReview { - /// The removed entry, or omitted if no entry matched. - [JsonPropertyName("entry")] - public ScheduleEntry? Entry { get; set; } + /// + [JsonIgnore] + public override string Resource => "skill"; + + /// The exact verified Skill action and its reviewed files. + [JsonPropertyName("review")] + public required SkillInstallationReview Review { get; set; } } -/// Identifier of the scheduled prompt to remove. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ScheduleStopRequest +/// One connection-owned, expiring request for a trusted host's explicit user decision. +public sealed class InstallationsConfirmRequest { - /// Id of the scheduled prompt to remove. - [JsonPropertyName("id")] - public long Id { get; set; } + /// Opaque one-use challenge. Return unchanged; never log or persist. + [JsonPropertyName("confirmationId")] + public string ConfirmationId { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Original plan expiry as an ISO 8601 timestamp. Confirmation never extends it. + [JsonPropertyName("expiresAt")] + public string ExpiresAt { get; set; } = string.Empty; -/// A bearer token supplied by the SDK client for a BYOK provider. The runtime sets it as `Authorization: Bearer <token>` on the outbound request and does no caching; the SDK consumer owns token caching and refresh. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderTokenAcquireResult -{ - /// The bearer token value (without the `Bearer ` prefix). - [JsonPropertyName("token")] - public string Token { get; set; } = string.Empty; -} + /// Random identifier of this installation operation, not a plan handle. + [JsonPropertyName("operationId")] + public string OperationId { get; set; } = string.Empty; -/// Asks the SDK client to acquire a bearer token for a BYOK provider whose config set `hasBearerTokenProvider: true`. Issued by the runtime before each outbound model request; the runtime does no caching, so this is sent once per request. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderTokenAcquireRequest -{ - /// Name of the BYOK provider needing a token. For the legacy whole-session provider this is the implicit provider name; for named providers it is the configured provider name. - [JsonPropertyName("providerName")] - public string ProviderName { get; set; } = string.Empty; + /// + /// Original engine-resolved selector for a bound operation, never a dispatch default. + /// Bound MCP confirmation always includes it; correlate it with the original pending action. + /// + [JsonPropertyName("policySessionId")] + public string? PolicySessionId { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Resource-specific review to present before collecting the user's decision. + [JsonPropertyName("review")] + public InstallationReview Review { get => field ??= new(); set; } -/// Result returned by an extension workflow closure. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowExecuteResult -{ - /// Workflow result value. - [JsonPropertyName("result")] - public JsonElement? Result { get; set; } + /// Opaque commitment to the exact review and inputs. Return unchanged; never log. + [JsonPropertyName("reviewFingerprint")] + public string ReviewFingerprint { get; set; } = string.Empty; } -/// Parameters sent to the owning extension to execute a workflow closure. +/// Closed set of public task kinds a connection can negotiate. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowExecuteRequest +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct TaskKind : IEquatable { - /// Workflow input value. - [JsonPropertyName("args")] - public JsonElement Args { get; set; } + private readonly string? _value; - /// Opaque token identifying this workflow execution attempt. - [JsonPropertyName("executionToken")] - public string ExecutionToken { get; set; } = string.Empty; + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public TaskKind(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// Registered workflow name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// Runtime-owned background agent task. + public static TaskKind Agent { get; } = new("agent"); - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Runtime-owned shell task. + public static TaskKind Shell { get; } = new("shell"); -/// Parameters for cooperatively aborting a workflow body. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowAbortRequest -{ - /// Opaque token identifying the execution attempt to abort. - [JsonPropertyName("executionToken")] - public string ExecutionToken { get; set; } = string.Empty; + /// Client-owned externally executed task. + public static TaskKind Client { get; } = new("client"); - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(TaskKind left, TaskKind right) => left.Equals(right); - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(TaskKind left, TaskKind right) => !(left == right); -/// Whether the client authoritatively confirmed its external work stopped. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ClientTaskCancelResult -{ - /// True only when the owner confirms that external work stopped before responding. - [JsonPropertyName("cancelled")] - public bool Cancelled { get; set; } -} + /// + public override bool Equals(object? obj) => obj is TaskKind other && Equals(other); -/// Runtime-to-owner cancellation request for a client-owned task. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ClientTaskCancelRequest -{ - /// Opaque identifier shared by coalesced cancellation callers. - [JsonPropertyName("cancellationId")] - public string CancellationId { get; set; } = string.Empty; + /// + public bool Equals(TaskKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// Owner-scoped task key included for correlation. - [JsonPropertyName("clientTaskId")] - public string ClientTaskId { get; set; } = string.Empty; + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// Canonical runtime-generated task identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// + public override string ToString() => Value; - /// Reason the runtime requests cancellation. - [JsonPropertyName("reason")] - public ClientTaskCancelReason Reason { get; set; } + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override TaskKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } - /// Session that owns the client task. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + public override void Write(Utf8JsonWriter writer, TaskKind value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskKind)); + } + } } -/// Describes a filesystem error. + +/// State of the persistent certificate authority of the sandbox credential proxy. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsError +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct SandboxProxyCaState : IEquatable { - /// Error classification. - [JsonPropertyName("code")] - public SessionFsErrorCode Code { get; set; } + private readonly string? _value; - /// Free-form detail about the error, for logging/diagnostics. - [JsonPropertyName("message")] - public string? Message { get; set; } + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public SandboxProxyCaState(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// For failed writeFile requests only: true if the provider changed the target before failing. Omit when unknown or unchanged. - [JsonPropertyName("writeChanged")] - public bool? WriteChanged { get; set; } -} + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; -/// File content as a UTF-8 string, or a filesystem error if the read failed. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsReadFileResult -{ - /// File content as UTF-8 string. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// This platform has no supported OS trust store. The proxy uses a per-process certificate bundle. + public static SandboxProxyCaState Unsupported { get; } = new("unsupported"); + + /// OS trust does not include the certificate authority, or none is stored. + public static SandboxProxyCaState NotInstalled { get; } = new("notInstalled"); + + /// OS trust includes the stored certificate authority. + public static SandboxProxyCaState Installed { get; } = new("installed"); + + /// The runtime could not read the certificate authority or the OS trust store. + public static SandboxProxyCaState Error { get; } = new("error"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SandboxProxyCaState left, SandboxProxyCaState right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SandboxProxyCaState left, SandboxProxyCaState right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is SandboxProxyCaState other && Equals(other); + + /// + public bool Equals(SandboxProxyCaState other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override SandboxProxyCaState Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } - /// Describes a filesystem error. - [JsonPropertyName("error")] - public SessionFsError? Error { get; set; } + /// + public override void Write(Utf8JsonWriter writer, SandboxProxyCaState value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SandboxProxyCaState)); + } + } } -/// Path of the file to read from the client-provided session filesystem. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsReadFileRequest -{ - /// Path using SessionFs conventions. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} -/// File bytes as standard base64, or a filesystem error if the read failed. +/// What the one-time staff update-channel migration did. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsReadFileBytesResult +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct StaffUpdateChannelMigrationStatus : IEquatable { - /// Exact file bytes encoded as standard base64. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + private readonly string? _value; - /// Describes a filesystem error. - [JsonPropertyName("error")] - public SessionFsError? Error { get; set; } -} + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public StaffUpdateChannelMigrationStatus(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } -/// Path of the binary file to read from the client-provided session filesystem. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsReadFileBytesRequest -{ - /// Path using SessionFs conventions. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// The account is not a staff account, so the migration does not apply and nothing was recorded. + public static StaffUpdateChannelMigrationStatus Ineligible { get; } = new("ineligible"); -/// File path, content to write, and optional mode for the client-provided session filesystem. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsWriteFileRequest -{ - /// Content to write. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// The migration already ran within the cutoff window; the stored completion timestamp is returned unchanged. + public static StaffUpdateChannelMigrationStatus Current { get; } = new("current"); - /// Optional POSIX-style mode for newly created files. - [JsonPropertyName("mode")] - public long? Mode { get; set; } + /// The persisted update channel was changed to stable. Only this outcome requires the host to reload its configuration. + public static StaffUpdateChannelMigrationStatus Migrated { get; } = new("migrated"); - /// Path using SessionFs conventions. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// The persisted channel was already stable, so nothing changed, but the migration is now recorded as done. + public static StaffUpdateChannelMigrationStatus Completed { get; } = new("completed"); - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// The account is on the experimental channel, which the migration deliberately leaves alone, and the migration is recorded as done. + public static StaffUpdateChannelMigrationStatus ExcludedExperimental { get; } = new("excluded-experimental"); -/// File path, standard-base64-encoded bytes to write, and optional mode for the client-provided session filesystem. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsWriteFileBytesRequest -{ - /// Exact file bytes encoded as standard base64. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(StaffUpdateChannelMigrationStatus left, StaffUpdateChannelMigrationStatus right) => left.Equals(right); - /// Optional POSIX-style mode for newly created files. - [JsonPropertyName("mode")] - public long? Mode { get; set; } + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(StaffUpdateChannelMigrationStatus left, StaffUpdateChannelMigrationStatus right) => !(left == right); - /// Path using SessionFs conventions. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// + public override bool Equals(object? obj) => obj is StaffUpdateChannelMigrationStatus other && Equals(other); - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// + public bool Equals(StaffUpdateChannelMigrationStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); -/// File path, content to append, and optional mode for the client-provided session filesystem. Implementations create parent directories as needed. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsAppendFileRequest -{ - /// Content to append. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// Optional POSIX-style mode for newly created files. - [JsonPropertyName("mode")] - public long? Mode { get; set; } + /// + public override string ToString() => Value; - /// Path using SessionFs conventions. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override StaffUpdateChannelMigrationStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + public override void Write(Utf8JsonWriter writer, StaffUpdateChannelMigrationStatus value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(StaffUpdateChannelMigrationStatus)); + } + } } -/// Indicates whether the requested path exists in the client-provided session filesystem. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsExistsResult -{ - /// Whether the path exists. - [JsonPropertyName("exists")] - public bool Exists { get; set; } -} -/// Path to test for existence in the client-provided session filesystem. +/// What the one-time staff log-level migration did. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsExistsRequest +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct StaffLogLevelMigrationStatus : IEquatable { - /// Path using SessionFs conventions. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + private readonly string? _value; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public StaffLogLevelMigrationStatus(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } -/// Filesystem metadata for the requested path, or a filesystem error if the stat failed. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsStatResult -{ - /// ISO 8601 timestamp of creation. - [JsonPropertyName("birthtime")] - public DateTimeOffset Birthtime { get; set; } + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// Describes a filesystem error. - [JsonPropertyName("error")] - public SessionFsError? Error { get; set; } + /// The account is not a staff account, so the migration does not apply and nothing was recorded. + public static StaffLogLevelMigrationStatus Ineligible { get; } = new("ineligible"); - /// Whether the path is a directory. - [JsonPropertyName("isDirectory")] - public bool IsDirectory { get; set; } + /// The migration already ran; the stored completion timestamp is returned unchanged. + public static StaffLogLevelMigrationStatus Current { get; } = new("current"); - /// Whether the path is a file. - [JsonPropertyName("isFile")] - public bool IsFile { get; set; } + /// The migration ran now. The persisted settings may have changed, so the host should adopt the returned settings when they are present. + public static StaffLogLevelMigrationStatus Refreshed { get; } = new("refreshed"); - /// ISO 8601 timestamp of last modification. - [JsonPropertyName("mtime")] - public DateTimeOffset Mtime { get; set; } + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(StaffLogLevelMigrationStatus left, StaffLogLevelMigrationStatus right) => left.Equals(right); - /// File size in bytes. - [JsonPropertyName("size")] - public long Size { get; set; } -} + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(StaffLogLevelMigrationStatus left, StaffLogLevelMigrationStatus right) => !(left == right); -/// Path whose metadata should be returned from the client-provided session filesystem. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsStatRequest -{ - /// Path using SessionFs conventions. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// + public override bool Equals(object? obj) => obj is StaffLogLevelMigrationStatus other && Equals(other); - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// + public bool Equals(StaffLogLevelMigrationStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); -/// Directory path to create in the client-provided session filesystem, with options for recursive creation and POSIX mode. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsMkdirRequest -{ - /// Optional POSIX-style mode for newly created directories. - [JsonPropertyName("mode")] - public long? Mode { get; set; } + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// Path using SessionFs conventions. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// + public override string ToString() => Value; - /// Create parent directories as needed. - [JsonPropertyName("recursive")] - public bool? Recursive { get; set; } + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override StaffLogLevelMigrationStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + public override void Write(Utf8JsonWriter writer, StaffLogLevelMigrationStatus value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(StaffLogLevelMigrationStatus)); + } + } } -/// Names of entries in the requested directory, or a filesystem error if the read failed. + +/// What the one-time obsolete-settings cleanup did to the document. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsReaddirResult +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct UserSettingsMigrateObsoleteOnDiskStatus : IEquatable { - /// Entry names in the directory. - [JsonPropertyName("entries")] - public IList Entries { get => field ??= []; set; } + private readonly string? _value; - /// Describes a filesystem error. - [JsonPropertyName("error")] - public SessionFsError? Error { get; set; } -} + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public UserSettingsMigrateObsoleteOnDiskStatus(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } -/// Directory path whose entries should be listed from the client-provided session filesystem. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsReaddirRequest -{ - /// Path using SessionFs conventions. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// No settings document exists, so there was nothing to clean. + public static UserSettingsMigrateObsoleteOnDiskStatus Missing { get; } = new("missing"); -/// Directory entry returned by session filesystem `readdirWithTypes`, with name and entry type. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsReaddirWithTypesEntry -{ - /// Entry name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// The document could not be parsed, so it was left untouched rather than rewritten from a partial read. + public static UserSettingsMigrateObsoleteOnDiskStatus Invalid { get; } = new("invalid"); - /// Entry type. - [JsonPropertyName("type")] - public SessionFsReaddirWithTypesEntryType Type { get; set; } -} + /// The document parsed and held nothing obsolete, so nothing was written. + public static UserSettingsMigrateObsoleteOnDiskStatus Unchanged { get; } = new("unchanged"); -/// Entries in the requested directory paired with file/directory type information, or a filesystem error if the read failed. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsReaddirWithTypesResult -{ - /// Directory entries with type information. - [JsonPropertyName("entries")] - public IList Entries { get => field ??= []; set; } + /// Obsolete keys were removed and the document was rewritten. + public static UserSettingsMigrateObsoleteOnDiskStatus Changed { get; } = new("changed"); - /// Describes a filesystem error. - [JsonPropertyName("error")] - public SessionFsError? Error { get; set; } -} + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(UserSettingsMigrateObsoleteOnDiskStatus left, UserSettingsMigrateObsoleteOnDiskStatus right) => left.Equals(right); -/// Directory path whose entries (with type information) should be listed from the client-provided session filesystem. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsReaddirWithTypesRequest -{ - /// Path using SessionFs conventions. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(UserSettingsMigrateObsoleteOnDiskStatus left, UserSettingsMigrateObsoleteOnDiskStatus right) => !(left == right); - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// + public override bool Equals(object? obj) => obj is UserSettingsMigrateObsoleteOnDiskStatus other && Equals(other); -/// Path to remove from the client-provided session filesystem, with options for recursive removal and force. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsRmRequest -{ - /// Ignore errors if the path does not exist. - [JsonPropertyName("force")] - public bool? Force { get; set; } + /// + public bool Equals(UserSettingsMigrateObsoleteOnDiskStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// Path using SessionFs conventions. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// Remove directories and their contents recursively. - [JsonPropertyName("recursive")] - public bool? Recursive { get; set; } + /// + public override string ToString() => Value; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override UserSettingsMigrateObsoleteOnDiskStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, UserSettingsMigrateObsoleteOnDiskStatus value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(UserSettingsMigrateObsoleteOnDiskStatus)); + } + } } -/// Source and destination paths for renaming or moving an entry in the client-provided session filesystem. + +/// Whether one atomic settings edit changed a file. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsRenameRequest +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct SettingsEditStatus : IEquatable { - /// Destination path using SessionFs conventions. - [JsonPropertyName("dest")] - public string Dest { get; set; } = string.Empty; + private readonly string? _value; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public SettingsEditStatus(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// Source path using SessionFs conventions. - [JsonPropertyName("src")] - public string Src { get; set; } = string.Empty; -} + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; -/// Query results including rows, columns, and rows affected, or a filesystem error if execution failed. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsSqliteQueryResult -{ - /// Column names from the result set. - [JsonPropertyName("columns")] - public IList Columns { get => field ??= []; set; } + /// The edit changed the setting and was stored. + public static SettingsEditStatus Changed { get; } = new("changed"); - /// Describes a filesystem error. - [JsonPropertyName("error")] - public SessionFsError? Error { get; set; } + /// The setting already had the requested state. + public static SettingsEditStatus Unchanged { get; } = new("unchanged"); - /// SQLite last_insert_rowid() value for INSERT. - [JsonPropertyName("lastInsertRowid")] - public long? LastInsertRowid { get; set; } + /// The existing settings file could not be edited safely; nothing was written. + public static SettingsEditStatus Invalid { get; } = new("invalid"); - /// For SELECT: array of row objects. For others: empty array. - [JsonPropertyName("rows")] - public IList> Rows { get => field ??= []; set; } + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SettingsEditStatus left, SettingsEditStatus right) => left.Equals(right); - /// Number of rows affected (for INSERT/UPDATE/DELETE). - [JsonPropertyName("rowsAffected")] - public long RowsAffected { get; set; } -} + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SettingsEditStatus left, SettingsEditStatus right) => !(left == right); -/// SQL query, query type, and optional bind parameters for executing a SQLite query against the per-session database. The provider applies its SQLite busy timeout for every call. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsSqliteQueryRequest -{ - /// Optional named bind parameters. - [JsonPropertyName("params")] - public IDictionary? Params { get; set; } + /// + public override bool Equals(object? obj) => obj is SettingsEditStatus other && Equals(other); - /// SQL query to execute. - [JsonPropertyName("query")] - public string Query { get; set; } = string.Empty; + /// + public bool Equals(SettingsEditStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// How to execute the query: 'exec' for DDL/multi-statement (no results), 'query' for SELECT (returns rows), 'run' for INSERT/UPDATE/DELETE (returns rowsAffected). - [JsonPropertyName("queryType")] - public SessionFsSqliteQueryType QueryType { get; set; } + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// + public override string ToString() => Value; -/// Classified SQLite transaction failure. busyOrLocked guarantees rollback; postCommitAmbiguous must never be retried. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsSqliteTransactionError -{ - /// Machine-readable classification of the transaction failure. - [JsonPropertyName("errorClass")] - public SessionFsSqliteTransactionErrorClass ErrorClass { get; set; } + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override SettingsEditStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } - /// Human-readable transaction failure message. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; + /// + public override void Write(Utf8JsonWriter writer, SettingsEditStatus value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SettingsEditStatus)); + } + } } -/// Per-statement results, or a classified transaction error. + +/// One atomic settings mutation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsSqliteTransactionResult +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct SettingsEditOperation : IEquatable { - /// Classified transaction failure, when execution did not succeed. - [JsonPropertyName("error")] - public SessionFsSqliteTransactionError? Error { get; set; } + private readonly string? _value; - /// Per-statement query results in input order. - [JsonPropertyName("results")] - public IList Results { get => field ??= []; set; } -} + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public SettingsEditOperation(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } -/// One statement in an atomic SQLite transaction. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsSqliteTransactionStatement -{ - /// Optional named bind parameters. - [JsonPropertyName("params")] - public IDictionary? Params { get; set; } + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// SQL statement to execute. - [JsonPropertyName("query")] - public string Query { get; set; } = string.Empty; + /// Set one scalar value. + public static SettingsEditOperation Set { get; } = new("set"); - /// How to execute the statement. - [JsonPropertyName("queryType")] - public SessionFsSqliteQueryType QueryType { get; set; } -} + /// Add one string to a list, unless present. + public static SettingsEditOperation AddItem { get; } = new("add-item"); -/// Statements to execute atomically. Providers apply busy handling for every call. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsSqliteTransactionRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Remove one string from a list, unless absent. + public static SettingsEditOperation RemoveItem { get; } = new("remove-item"); - /// Ordered SQL statements to execute in one transaction. - [JsonPropertyName("statements")] - public IList Statements { get => field ??= []; set; } -} + /// Remove the setting without supplying a value. + public static SettingsEditOperation Remove { get; } = new("remove"); -/// Indicates whether the per-session SQLite database already exists. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsSqliteExistsResult -{ - /// Whether the session database already exists. - [JsonPropertyName("exists")] - public bool Exists { get; set; } -} + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SettingsEditOperation left, SettingsEditOperation right) => left.Equals(right); -/// Identifies the target session. -public sealed class SessionFsSqliteExistsRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SettingsEditOperation left, SettingsEditOperation right) => !(left == right); -/// Canvas open result returned by the provider. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CanvasProviderOpenResult -{ - /// Provider-supplied status text. - [JsonPropertyName("status")] - public string? Status { get; set; } + /// + public override bool Equals(object? obj) => obj is SettingsEditOperation other && Equals(other); - /// Provider-supplied title. - [JsonPropertyName("title")] - public string? Title { get; set; } + /// + public bool Equals(SettingsEditOperation other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// URL for web-rendered canvases. - [JsonPropertyName("url")] - public string? Url { get; set; } -} + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); -/// Host capabilities. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CanvasHostContextCapabilities -{ - /// Whether canvas rendering is supported. - [JsonPropertyName("canvases")] - public bool? Canvases { get; set; } -} + /// + public override string ToString() => Value; -/// Host context supplied by the runtime. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CanvasHostContext -{ - /// Host capabilities. - [JsonPropertyName("capabilities")] - public CanvasHostContextCapabilities? Capabilities { get; set; } -} + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override SettingsEditOperation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } -/// Session context supplied by the runtime. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CanvasSessionContext -{ - /// Active session working directory, when known. - [JsonPropertyName("workingDirectory")] - public string? WorkingDirectory { get; set; } + /// + public override void Write(Utf8JsonWriter writer, SettingsEditOperation value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SettingsEditOperation)); + } + } } -/// Canvas open parameters sent to the provider. + +/// Which repository settings file to edit. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CanvasProviderOpenRequest -{ - /// Provider-local canvas identifier. - [JsonPropertyName("canvasId")] - public string CanvasId { get; set; } = string.Empty; +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct RepoSettingsEditScope : IEquatable +{ + private readonly string? _value; - /// Owning provider identifier. - [JsonPropertyName("extensionId")] - public string ExtensionId { get; set; } = string.Empty; + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public RepoSettingsEditScope(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// Host context supplied by the runtime. - [JsonPropertyName("host")] - public CanvasHostContext? Host { get; set; } + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// Canvas open input. - [JsonPropertyName("input")] - public JsonElement? Input { get; set; } + /// The shared repository settings file. + public static RepoSettingsEditScope Repo { get; } = new("repo"); - /// Stable caller-supplied canvas instance identifier. - [JsonPropertyName("instanceId")] - public string InstanceId { get; set; } = string.Empty; + /// The local repository settings file. + public static RepoSettingsEditScope Local { get; } = new("local"); - /// Session context supplied by the runtime. - [JsonPropertyName("session")] - public CanvasSessionContext? Session { get; set; } + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(RepoSettingsEditScope left, RepoSettingsEditScope right) => left.Equals(right); - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(RepoSettingsEditScope left, RepoSettingsEditScope right) => !(left == right); -/// Canvas close parameters sent to the provider. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CanvasProviderCloseRequest -{ - /// Provider-local canvas identifier. - [JsonPropertyName("canvasId")] - public string CanvasId { get; set; } = string.Empty; + /// + public override bool Equals(object? obj) => obj is RepoSettingsEditScope other && Equals(other); - /// Owning provider identifier. - [JsonPropertyName("extensionId")] - public string ExtensionId { get; set; } = string.Empty; + /// + public bool Equals(RepoSettingsEditScope other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// Host context supplied by the runtime. - [JsonPropertyName("host")] - public CanvasHostContext? Host { get; set; } + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// Canvas instance identifier. - [JsonPropertyName("instanceId")] - public string InstanceId { get; set; } = string.Empty; + /// + public override string ToString() => Value; - /// Session context supplied by the runtime. - [JsonPropertyName("session")] - public CanvasSessionContext? Session { get; set; } + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override RepoSettingsEditScope Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + public override void Write(Utf8JsonWriter writer, RepoSettingsEditScope value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(RepoSettingsEditScope)); + } + } } -/// Canvas action invocation parameters sent to the provider. + +/// The population a feature flag is declared for, before overrides. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CanvasProviderInvokeActionRequest +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct FeatureFlagAvailability : IEquatable { - /// Action name to invoke. - [JsonPropertyName("actionName")] - public string ActionName { get; set; } = string.Empty; + private readonly string? _value; - /// Provider-local canvas identifier. - [JsonPropertyName("canvasId")] - public string CanvasId { get; set; } = string.Empty; + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public FeatureFlagAvailability(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// Owning provider identifier. - [JsonPropertyName("extensionId")] - public string ExtensionId { get; set; } = string.Empty; + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// Host context supplied by the runtime. - [JsonPropertyName("host")] - public CanvasHostContext? Host { get; set; } + /// Enabled for everyone, before any override. + public static FeatureFlagAvailability On { get; } = new("on"); - /// Action input. - [JsonPropertyName("input")] - public JsonElement? Input { get; set; } + /// Enabled only for accounts on the owning team. + public static FeatureFlagAvailability Team { get; } = new("team"); - /// Canvas instance identifier. - [JsonPropertyName("instanceId")] - public string InstanceId { get; set; } = string.Empty; + /// Enabled only for staff accounts that have also opted into experimental features. + public static FeatureFlagAvailability StaffAndExperimental { get; } = new("staff-and-experimental"); - /// Session context supplied by the runtime. - [JsonPropertyName("session")] - public CanvasSessionContext? Session { get; set; } + /// Enabled for staff accounts and for accounts that opted into experimental features. + public static FeatureFlagAvailability StaffOrExperimental { get; } = new("staff-or-experimental"); - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Enabled for any staff account, GitHub or Microsoft. + public static FeatureFlagAvailability Staff { get; } = new("staff"); -/// Reports a supervised listener's hosting-task termination and cleanup outcome. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HostExitedRequest -{ - /// Explicit startup or teardown failure, when present. - [JsonPropertyName("error")] - public string? Error { get; set; } + /// Enabled for GitHub staff accounts only. + public static FeatureFlagAvailability StaffGitHub { get; } = new("staff-github"); - /// Process exit status when available; absent for in-process listener tasks. - [JsonPropertyName("exitCode")] - public long? ExitCode { get; set; } + /// Enabled for Microsoft staff accounts only. + public static FeatureFlagAvailability StaffMicrosoft { get; } = new("staff-microsoft"); - /// Listener UUID. - [JsonPropertyName("hostId")] - public string HostId { get; set; } = string.Empty; + /// Enabled only for accounts that opted into experimental features. + public static FeatureFlagAvailability Experimental { get; } = new("experimental"); - /// Cause of termination. - [JsonPropertyName("reason")] - public HostExitReason Reason { get; set; } -} + /// Disabled for everyone, before any override. + public static FeatureFlagAvailability Off { get; } = new("off"); -/// Opaque integrator-owned process launch profile for one extension entrypoint. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ExtensionLaunchProfile -{ - /// Opaque integrator-defined arguments passed to the executable. The runtime does not append the extension entrypoint. - [JsonPropertyName("args")] - public IList Args { get => field ??= []; set; } + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(FeatureFlagAvailability left, FeatureFlagAvailability right) => left.Equals(right); - /// Opaque integrator-defined environment variables. Runtime-owned COPILOT_SDK_PATH, SESSION_ID, and COPILOT_EXTENSION_PARENT_PID values take precedence. - [JsonPropertyName("env")] - public IDictionary Env { get => field ??= new Dictionary(); set; } + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(FeatureFlagAvailability left, FeatureFlagAvailability right) => !(left == right); - /// Executable used to launch the extension entrypoint. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("executable")] - public string Executable { get; set; } = string.Empty; -} + /// + public override bool Equals(object? obj) => obj is FeatureFlagAvailability other && Equals(other); -/// The launch profile for a supported entrypoint. Omit launch when the provider does not support the entrypoint. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ExtensionLaunchProviderResolveResult -{ - /// Opaque launch profile, omitted when this provider does not support the entrypoint. - [JsonPropertyName("launch")] - public ExtensionLaunchProfile? Launch { get; set; } -} + /// + public bool Equals(FeatureFlagAvailability other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); -/// A discovered extension entrypoint that the registered integrator may classify and resolve to an opaque launch profile. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ExtensionLaunchProviderResolveRequest -{ - /// Source-qualified extension identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// Absolute path to the discovered extension entrypoint. - [JsonPropertyName("modulePath")] - public string ModulePath { get; set; } = string.Empty; + /// + public override string ToString() => Value; - /// Human-readable extension name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override FeatureFlagAvailability Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } - /// Discovery source for the extension entrypoint. - [JsonPropertyName("source")] - public ExtensionSource Source { get; set; } + /// + public override void Write(Utf8JsonWriter writer, FeatureFlagAvailability value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(FeatureFlagAvailability)); + } + } } -/// Acknowledgement. Returning successfully simply means the SDK accepted the start frame; it does not imply the request will succeed. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class LlmInferenceHttpRequestStartResult -{ -} -/// The head of an outbound model-layer HTTP request. +/// Which layer disabled bypass-permissions mode. The layer matters because only a server-delivered disable is pinned to an account and can later be lifted by signing in elsewhere. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class LlmInferenceHttpRequestStartRequest +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ManagedSettingsBypassDisableSource : IEquatable { - /// Stable identity of the agent trajectory that issued this request. Present when the request originates from an agent turn; absent for requests outside any agent context. This is the same identity used by lifecycle and bridged session events and remains constant across turns and retries. - [JsonPropertyName("agentId")] - public string? AgentId { get; set; } + private readonly string? _value; - /// Identity of the agent invocation (one agentic loop) that issued this request. It remains fixed across physical retries within the invocation and is distinct from the stable trajectory `agentId`. A caller-supplied invocation id always takes precedence (this covers auxiliary calls that have no model call id). Otherwise, first-party CAPI requests fall back to the runtime's agent task id — the same value the runtime emits as the `X-Agent-Task-Id` header — while custom-provider requests fall back to the model call id. - [JsonPropertyName("agentInvocationId")] - public string? AgentInvocationId { get; set; } + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ManagedSettingsBypassDisableSource(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// HTTP request headers, preserving multiple values per name. - [JsonPropertyName("headers")] - public IDictionary> Headers { get => field ??= new Dictionary>(); set; } + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// Coarse classification of the interaction that produced this request. Open string for forward-compatibility; known values include `conversation-agent`, `conversation-subagent`, `conversation-sampling`, `conversation-background`, `conversation-compaction`, and `conversation-user`. Absent when the runtime did not classify the request. Comes from the runtime's per-request agent context independently of transport; on the CAPI transport the runtime derives the upstream `X-Interaction-Type` header from this same context. - [JsonPropertyName("interactionType")] - public string? InteractionType { get; set; } + /// The user turned bypass mode off themselves. + public static ManagedSettingsBypassDisableSource User { get; } = new("user"); - /// HTTP method, e.g. GET, POST. - [JsonPropertyName("method")] - public string Method { get; set; } = string.Empty; + /// The account's server-delivered org policy disabled it. Pinned to the account named by `currentManagedIdentity`. + public static ManagedSettingsBypassDisableSource Managed { get; } = new("managed"); - /// Stable identity of the immediate parent trajectory. Present for child trajectories such as subagents and conversation-sampling requests; absent for root-agent and non-agent requests. - [JsonPropertyName("parentAgentId")] - public string? ParentAgentId { get; set; } + /// The device/MDM layer disabled it. Outranks the account, so no server answer lifts it. + public static ManagedSettingsBypassDisableSource Mdm { get; } = new("mdm"); - /// Opaque runtime-minted id, unique per in-flight request. The SDK uses this to correlate httpRequestChunk frames and to address its httpResponseStart / httpResponseChunk replies back to the runtime. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ManagedSettingsBypassDisableSource left, ManagedSettingsBypassDisableSource right) => left.Equals(right); - /// Id of the runtime session that triggered this request, when one is in scope. Absent for requests issued outside any session (e.g. startup model-catalog or capability resolution). This is a payload field — not a dispatch key — because the client-global API is registered process-wide rather than per session. - [JsonPropertyName("sessionId")] - public string? SessionId { get; set; } + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ManagedSettingsBypassDisableSource left, ManagedSettingsBypassDisableSource right) => !(left == right); - /// Transport the runtime would otherwise use for this request. `http` (the default when absent) covers plain HTTP and SSE responses; `websocket` indicates a full-duplex message channel where each body chunk maps to one WebSocket message and the `binary` flag distinguishes text from binary frames. The SDK consumer uses this to decide whether to service the request with an HTTP client or a WebSocket client. It is the one piece of request metadata the consumer cannot reliably infer from the URL or headers alone. - [JsonPropertyName("transport")] - public LlmInferenceHttpRequestStartTransport? Transport { get; set; } + /// + public override bool Equals(object? obj) => obj is ManagedSettingsBypassDisableSource other && Equals(other); - /// Absolute request URL. - [JsonPropertyName("url")] - public string Url { get; set; } = string.Empty; -} + /// + public bool Equals(ManagedSettingsBypassDisableSource other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); -/// Acknowledgement. The SDK is free to ignore the ack and treat chunk delivery as fire-and-forget. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class LlmInferenceHttpRequestChunkResult -{ + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ManagedSettingsBypassDisableSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ManagedSettingsBypassDisableSource value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ManagedSettingsBypassDisableSource)); + } + } } -/// A request body chunk or cancellation signal. + +/// What the managed-policy resolution said about bypass-permissions mode. `unknown` is a third answer rather than a missing one, and the caller must send it explicitly when it has no verdict. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class LlmInferenceHttpRequestChunkRequest +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ManagedSettingsBypassPolicyResolution : IEquatable { - /// Identity of the agent invocation (one agentic loop) this body chunk belongs to, matching the `agentInvocationId` semantics on httpRequestStart. Carried per chunk so a persistent transport can attribute successive turns correctly: when a WebSocket connection is reused across turns, the httpRequestStart identity reflects only the turn that opened the connection, so each later turn stamps its own invocation id here. Absent when the runtime has no invocation context for the request, or on the plain-HTTP transport where every request has its own httpRequestStart. - [JsonPropertyName("agentInvocationId")] - public string? AgentInvocationId { get; set; } + private readonly string? _value; - /// When true, `data` is base64-encoded bytes. When absent or false, `data` is UTF-8 text. - [JsonPropertyName("binary")] - public bool? Binary { get; set; } + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ManagedSettingsBypassPolicyResolution(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// When true, the runtime is cancelling the in-flight request (e.g. upstream consumer aborted). `data` is ignored. Implies end-of-request. - [JsonPropertyName("cancel")] - public bool? Cancel { get; set; } + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// Optional human-readable reason for the cancellation, propagated for logging. - [JsonPropertyName("cancelReason")] - public string? CancelReason { get; set; } + /// The policy resolved and does not disable bypass mode. + public static ManagedSettingsBypassPolicyResolution Allowed { get; } = new("allowed"); - /// Body byte range. UTF-8 text when `binary` is absent or false; base64-encoded bytes when `binary` is true. May be empty. - [JsonPropertyName("data")] - public string Data { get; set; } = string.Empty; + /// The policy resolved and disables bypass mode. + public static ManagedSettingsBypassPolicyResolution Disabled { get; } = new("disabled"); - /// When true, this is the final body chunk for the request. The SDK may rely on having received an end-marked chunk before treating the request body as complete. - [JsonPropertyName("end")] - public bool? End { get; set; } + /// No verdict: the fetch failed, or the service declared a policy this build may not enforce. Whatever is in force is preserved, because reverting to permissive on an unreadable policy would widen access. + public static ManagedSettingsBypassPolicyResolution Unknown { get; } = new("unknown"); - /// Matches the requestId from the originating httpRequestStart frame. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ManagedSettingsBypassPolicyResolution left, ManagedSettingsBypassPolicyResolution right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ManagedSettingsBypassPolicyResolution left, ManagedSettingsBypassPolicyResolution right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is ManagedSettingsBypassPolicyResolution other && Equals(other); + + /// + public bool Equals(ManagedSettingsBypassPolicyResolution other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ManagedSettingsBypassPolicyResolution Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ManagedSettingsBypassPolicyResolution value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ManagedSettingsBypassPolicyResolution)); + } + } } -/// Client environment metadata describing the process that produced a telemetry event. + +/// Severity of a diagnostic emitted while ingesting managed settings. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class GitHubTelemetryClientInfo +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ManagedSettingsLogLevel : IEquatable { - /// Copilot CLI version string. - [JsonPropertyName("cli_version")] - public string CliVersion { get; set; } = string.Empty; + private readonly string? _value; - /// Name of the client application. - [JsonPropertyName("client_name")] - public string? ClientName { get; set; } + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ManagedSettingsLogLevel(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// Type of client. - [JsonPropertyName("client_type")] - public string? ClientType { get; set; } + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// Copilot subscription plan, when known. - [JsonPropertyName("copilot_plan")] - public string? CopilotPlan { get; set; } + /// Detail useful only when diagnosing an ingestion. + public static ManagedSettingsLogLevel Debug { get; } = new("debug"); - /// Number of logical CPU cores on the host. - [JsonPropertyName("cpu_count")] - public long? CpuCount { get; set; } + /// Ordinary progress worth surfacing under a verbose flag. + public static ManagedSettingsLogLevel Info { get; } = new("info"); - /// Distinct CPU model names for the host, comma-separated. - [JsonPropertyName("cpu_model")] - public string? CpuModel { get; set; } + /// A condition the operator should see but which did not stop the ingestion. + public static ManagedSettingsLogLevel Warning { get; } = new("warning"); - /// Stable machine identifier for the device. - [JsonPropertyName("dev_device_id")] - public string? DevDeviceId { get; set; } + /// A failure that changed the outcome of the ingestion. + public static ManagedSettingsLogLevel Error { get; } = new("error"); - /// Whether the user is a GitHub/Microsoft staff member. - [JsonPropertyName("is_staff")] - public bool? IsStaff { get; set; } + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ManagedSettingsLogLevel left, ManagedSettingsLogLevel right) => left.Equals(right); - /// Node.js runtime version string. - [JsonPropertyName("node_version")] - public string NodeVersion { get; set; } = string.Empty; + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ManagedSettingsLogLevel left, ManagedSettingsLogLevel right) => !(left == right); - /// Operating system architecture (e.g. arm64, x64). - [JsonPropertyName("os_arch")] - public string OsArch { get; set; } = string.Empty; + /// + public override bool Equals(object? obj) => obj is ManagedSettingsLogLevel other && Equals(other); - /// Operating system platform (e.g. darwin, linux, win32). - [JsonPropertyName("os_platform")] - public string OsPlatform { get; set; } = string.Empty; + /// + public bool Equals(ManagedSettingsLogLevel other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// Operating system version string. - [JsonPropertyName("os_version")] - public string OsVersion { get; set; } = string.Empty; + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ManagedSettingsLogLevel Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ManagedSettingsLogLevel value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ManagedSettingsLogLevel)); + } + } } -/// A single telemetry event in the runtime's native GitHub-shaped telemetry format, forwarded verbatim to opted-in hosts. The `restricted` flag on the enclosing GitHubTelemetryNotification distinguishes standard from restricted events; the payload shape is identical for both. + +/// Which channel registered the policy helper that produced a helper layer. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class GitHubTelemetryEvent +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ManagedSettingsPolicyHelperAuthority : IEquatable { - /// Client environment metadata. - [JsonPropertyName("client")] - public GitHubTelemetryClientInfo? Client { get; set; } + private readonly string? _value; - /// Copilot tracking ID for user-level attribution. - [JsonPropertyName("copilot_tracking_id")] - public string? CopilotTrackingId { get; set; } + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ManagedSettingsPolicyHelperAuthority(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// Timestamp when the event was created (ISO 8601 format). - [JsonPropertyName("created_at")] - public string? CreatedAt { get; set; } + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// Experiment assignment context. - [JsonPropertyName("exp_assignment_context")] - public string? ExpAssignmentContext { get; set; } + /// The helper was registered by the device (MDM or managed file) layer. + public static ManagedSettingsPolicyHelperAuthority Device { get; } = new("device"); - /// Feature flags enabled for this session, as a map from flag to value. - [JsonPropertyName("features")] - public IDictionary? Features { get; set; } + /// The helper was registered by the account's server-delivered policy. + public static ManagedSettingsPolicyHelperAuthority Server { get; } = new("server"); - /// Event type/kind (e.g. get_completion_with_tools_turn, tool_call_executed). - [JsonPropertyName("kind")] - public string Kind { get; set; } = string.Empty; + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ManagedSettingsPolicyHelperAuthority left, ManagedSettingsPolicyHelperAuthority right) => left.Equals(right); - /// Numeric metrics as a map from key to value. - [JsonPropertyName("metrics")] - public IDictionary Metrics { get => field ??= new Dictionary(); set; } + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ManagedSettingsPolicyHelperAuthority left, ManagedSettingsPolicyHelperAuthority right) => !(left == right); - /// Reference to the model call that produced this event. - [JsonPropertyName("model_call_id")] - public string? ModelCallId { get; set; } + /// + public override bool Equals(object? obj) => obj is ManagedSettingsPolicyHelperAuthority other && Equals(other); - /// String-valued properties as a map from key to value. - [JsonPropertyName("properties")] - public IDictionary Properties { get => field ??= new Dictionary(); set; } + /// + public bool Equals(ManagedSettingsPolicyHelperAuthority other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// Session identifier the event belongs to. - [JsonPropertyName("session_id")] - public string? SessionId { get; set; } -} + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); -/// Payload for a `gitHubTelemetry.event` notification: a single GitHub telemetry event the runtime forwards to a host connection that opted into telemetry forwarding during the `server.connect` handshake. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class GitHubTelemetryNotification -{ - /// The telemetry event, in the runtime's native GitHub-shaped telemetry format. - [JsonPropertyName("event")] - public GitHubTelemetryEvent Event { get => field ??= new(); set; } + /// + public override string ToString() => Value; - /// Whether this is a restricted telemetry event (cli.restricted_telemetry). Hosts must route restricted events to first-party Microsoft stores only. - [JsonPropertyName("restricted")] - public bool Restricted { get; set; } + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ManagedSettingsPolicyHelperAuthority Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } - /// Session the telemetry event belongs to, when it is session-scoped. Omitted for sessionless events (for example, `server.sendTelemetry` calls with no session id), which are still forwarded to opted-in connections. - [JsonPropertyName("sessionId")] - public string? SessionId { get; set; } + /// + public override void Write(Utf8JsonWriter writer, ManagedSettingsPolicyHelperAuthority value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ManagedSettingsPolicyHelperAuthority)); + } + } } -/// SDK host response to a GitHub credential request. -/// Polymorphic base type discriminated by kind. + +/// How the server layer of an ingestion was resolved. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(GitHubTokenAcquireResultToken), "token")] -[JsonDerivedType(typeof(GitHubTokenAcquireResultCancelled), "cancelled")] -public partial class GitHubTokenAcquireResult +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ManagedSettingsServerPolicyResolution : IEquatable { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + private readonly string? _value; + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ManagedSettingsServerPolicyResolution(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } -/// The token variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class GitHubTokenAcquireResultToken : GitHubTokenAcquireResult -{ - /// - [JsonIgnore] - public override string Kind => "token"; + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// GitHub access token acquired by the SDK host. - [JsonPropertyName("accessToken")] - public required string AccessToken { get; set; } + /// A live fetch succeeded. + public static ManagedSettingsServerPolicyResolution Live { get; } = new("live"); - /// Remaining token lifetime in seconds when callback execution completes. It must exceed the one-hour preflight refresh threshold. - [JsonPropertyName("expiresIn")] - public required long ExpiresIn { get; set; } + /// A recent cached entry was served without a live fetch. + public static ManagedSettingsServerPolicyResolution FreshCache { get; } = new("freshCache"); - /// OAuth token type. Defaults to bearer when omitted. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("tokenType")] - public string? TokenType { get; set; } -} + /// No cached policy or confirmed no-policy result exists. + public static ManagedSettingsServerPolicyResolution CacheMiss { get; } = new("cacheMiss"); + + /// A live fetch failed and a cached entry was served in its place. + public static ManagedSettingsServerPolicyResolution FallbackCache { get; } = new("fallbackCache"); + + /// No policy could be resolved and the host proceeds unmanaged. + public static ManagedSettingsServerPolicyResolution FailOpen { get; } = new("failOpen"); + + /// No policy could be resolved and the host applies the fail-closed gates. + public static ManagedSettingsServerPolicyResolution FailClosed { get; } = new("failClosed"); + + /// No fetch was attempted because no usable token was available. + public static ManagedSettingsServerPolicyResolution SkippedNoToken { get; } = new("skippedNoToken"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ManagedSettingsServerPolicyResolution left, ManagedSettingsServerPolicyResolution right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ManagedSettingsServerPolicyResolution left, ManagedSettingsServerPolicyResolution right) => !(left == right); -/// The cancelled variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class GitHubTokenAcquireResultCancelled : GitHubTokenAcquireResult -{ /// - [JsonIgnore] - public override string Kind => "cancelled"; + public override bool Equals(object? obj) => obj is ManagedSettingsServerPolicyResolution other && Equals(other); + + /// + public bool Equals(ManagedSettingsServerPolicyResolution other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ManagedSettingsServerPolicyResolution Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ManagedSettingsServerPolicyResolution value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ManagedSettingsServerPolicyResolution)); + } + } } -/// Asks the SDK client to acquire a GitHub access token from an opaque callback registration. + +/// The operation that requires a policy-helper response. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class GitHubTokenAcquireRequest +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ManagedSettingsPolicyHelperReason : IEquatable { - /// Effective GitHub host for which the callback must return a token. - [JsonPropertyName("host")] - public string Host { get; set; } = string.Empty; - - /// Why the runtime is requesting a GitHub credential. - [JsonPropertyName("reason")] - public GitHubTokenAcquireReason Reason { get; set; } - - /// Opaque identifier generated by the SDK for this callback registration. - [JsonPropertyName("registrationId")] - public string RegistrationId { get; set; } = string.Empty; + private readonly string? _value; - /// Session receiving the token. Absent only before a cloud session has been assigned its id. - [JsonPropertyName("sessionId")] - public string? SessionId { get; set; } -} + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ManagedSettingsPolicyHelperReason(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } -/// A response is meaningful only on the connection and request that issued its challenge. -public sealed class InstallationsConfirmResult -{ - /// Exact challenge from the request. - [JsonPropertyName("confirmationId")] - public string ConfirmationId { get; set; } = string.Empty; + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// Fresh explicit user decision. There is no default. - [JsonPropertyName("decision")] - public InstallationDecision Decision { get; set; } + /// Resolve policy for session startup. + public static ManagedSettingsPolicyHelperReason SessionStart { get; } = new("sessionStart"); - /// Exact review commitment from the request. - [JsonPropertyName("reviewFingerprint")] - public string ReviewFingerprint { get; set; } = string.Empty; -} + /// Resolve policy before plugin activation. + public static ManagedSettingsPolicyHelperReason PluginActivation { get; } = new("pluginActivation"); -/// Only resource kinds with an implemented installation engine have a review variant. -/// Polymorphic base type discriminated by resource. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "resource", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(InstallationReviewMcp), "mcp")] -[JsonDerivedType(typeof(InstallationReviewSkill), "skill")] -public partial class InstallationReview -{ - /// The type discriminator. - [JsonPropertyName("resource")] - public virtual string Resource { get; set; } = string.Empty; -} + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ManagedSettingsPolicyHelperReason left, ManagedSettingsPolicyHelperReason right) => left.Equals(right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ManagedSettingsPolicyHelperReason left, ManagedSettingsPolicyHelperReason right) => !(left == right); -/// Safe MCP review fields. No raw card, retrieval URL, plan handle or secret value. -/// Polymorphic base type discriminated by action. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "action", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(McpInstallationReviewInstall), "install")] -[JsonDerivedType(typeof(McpInstallationReviewUninstall), "uninstall")] -public partial class McpInstallationReview -{ - /// The type discriminator. - [JsonPropertyName("action")] - public virtual string Action { get; set; } = string.Empty; -} + /// + public override bool Equals(object? obj) => obj is ManagedSettingsPolicyHelperReason other && Equals(other); + /// + public bool Equals(ManagedSettingsPolicyHelperReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); -/// -/// Final remote configuration, not a template. The producer refuses external-value -/// expansion before presenting this review. Receipt-owned secrets appear only as -/// `${installation-secret:<id>}` references whose `<id>` matches a reviewed -/// `${secret:<id>}` placeholder; values are never included. -/// -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpInstallationRemoteConfiguration -{ - /// - /// Configured headers, excluding separately authorised OAuth tokens. Values may - /// contain owned secret references, never secret values. - /// - [JsonPropertyName("headers")] - public IDictionary Headers { get => field ??= new Dictionary(); set; } + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// Configured tool selection, not permission to invoke those tools. - [JsonPropertyName("tools")] - public IList Tools { get => field ??= []; set; } + /// + public override string ToString() => Value; - /// Transport in the effective persisted remote configuration. - [JsonPropertyName("transport")] - public McpPlanRemoteTransport Transport { get; set; } + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ManagedSettingsPolicyHelperReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } - /// Exact resolved endpoint, without templates or secret placeholders. - [JsonPropertyName("url")] - public string Url { get; set; } = string.Empty; + /// + public override void Write(Utf8JsonWriter writer, ManagedSettingsPolicyHelperReason value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ManagedSettingsPolicyHelperReason)); + } + } } -/// The install variant of . + +/// How an ingestion may use the on-disk server-policy cache. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationReviewInstall : McpInstallationReview +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ManagedSettingsServerPolicyCacheMode : IEquatable { - /// - [JsonIgnore] - public override string Action => "install"; + private readonly string? _value; - /// Catalogue identity retained from the bound candidate when available. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("catalogue")] - public InstallationCatalogueIdentity? Catalogue { get; set; } + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ManagedSettingsServerPolicyCacheMode(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// Original catalogue trust metadata, not a verification claim. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("catalogueTrust")] - public CatalogTrustSnapshot? CatalogueTrust { get; set; } + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// The configuration change for the selected alternative only. - [JsonPropertyName("configurationChange")] - public required McpPlanConfigurationChange ConfigurationChange { get; set; } + /// Serve a recent cache entry before a live fetch. + public static ManagedSettingsServerPolicyCacheMode PreferFresh { get; } = new("preferFresh"); - /// - /// Complete effective remote configuration for final installation review. - /// Earlier private selection reviews and package choices omit this field. - /// The owned remote resource requires it before issuing confirmation. - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("effectiveConfiguration")] - public McpInstallationRemoteConfiguration? EffectiveConfiguration { get; set; } + /// Read cached policy without a network request. + public static ManagedSettingsServerPolicyCacheMode CacheOnly { get; } = new("cacheOnly"); - /// Identity from the retained plan, not caller display text. - [JsonPropertyName("identity")] - public required McpPlanResourceIdentity Identity { get; set; } + /// Serve a cached entry only as a fallback after a failed live fetch. + public static ManagedSettingsServerPolicyCacheMode FallbackOnly { get; } = new("fallbackOnly"); - /// Non-secret values supplied for this selected alternative. - [JsonPropertyName("inputs")] - public required IList Inputs { get; set; } + /// Ignore the cache entirely and rely on the live fetch alone. + public static ManagedSettingsServerPolicyCacheMode Disabled { get; } = new("disabled"); - /// Policy decision bound to this plan. - [JsonPropertyName("policy")] - public required McpPlanPolicyResult Policy { get; set; } + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ManagedSettingsServerPolicyCacheMode left, ManagedSettingsServerPolicyCacheMode right) => left.Equals(right); - /// Original source identity and content commitment. - [JsonPropertyName("provenance")] - public required McpPlanProvenance Provenance { get; set; } + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ManagedSettingsServerPolicyCacheMode left, ManagedSettingsServerPolicyCacheMode right) => !(left == right); - /// Explicit reviewed backend selection; no backend is accessed when no secrets are supplied. - [JsonPropertyName("secretStorage")] - public required McpInstallationSecretStorage SecretStorage { get; set; } + /// + public override bool Equals(object? obj) => obj is ManagedSettingsServerPolicyCacheMode other && Equals(other); - /// Only the selected alternative is applied. - [JsonPropertyName("selectedChoice")] - public required McpPlanTransportChoice SelectedChoice { get; set; } + /// + public bool Equals(ManagedSettingsServerPolicyCacheMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// Exact reviewed placeholders supplied separately. Never secret values. - [JsonPropertyName("suppliedSecrets")] - public required IList SuppliedSecrets { get; set; } + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// Exact reviewed user-scope destination. - [JsonPropertyName("target")] - public required McpPlanTarget Target { get; set; } + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ManagedSettingsServerPolicyCacheMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ManagedSettingsServerPolicyCacheMode value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ManagedSettingsServerPolicyCacheMode)); + } + } } -/// The uninstall variant of . + +/// Severity of a managed-settings validation finding. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationReviewUninstall : McpInstallationReview +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ManagedSettingsDiagnosticSeverity : IEquatable { - /// - [JsonIgnore] - public override string Action => "uninstall"; + private readonly string? _value; - /// Identity from the installed receipt. - [JsonPropertyName("identity")] - public required McpPlanResourceIdentity Identity { get; set; } + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ManagedSettingsDiagnosticSeverity(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// Receipt-owned installation being removed. - [JsonPropertyName("installationId")] - public required string InstallationId { get; set; } + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// Exact planner-owned secret slots to remove, excluding shared OAuth grants. - [JsonPropertyName("ownedSecretCount")] - public required long OwnedSecretCount { get; set; } + /// The runtime rejects the document. + public static ManagedSettingsDiagnosticSeverity Error { get; } = new("error"); - /// Current removal policy, independent of permission to activate the server. - [JsonPropertyName("policy")] - public required McpPlanPolicyResult Policy { get; set; } + /// The runtime accepts the document but ignores the flagged content. + public static ManagedSettingsDiagnosticSeverity Warning { get; } = new("warning"); - /// Shared profile authentication is deliberately retained, not pending cleanup. - [JsonPropertyName("preservesSharedAuthentication")] - public required bool PreservesSharedAuthentication { get; set; } + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ManagedSettingsDiagnosticSeverity left, ManagedSettingsDiagnosticSeverity right) => left.Equals(right); - /// Source identity and content commitment retained by the installed receipt. - [JsonPropertyName("provenance")] - public required McpPlanProvenance Provenance { get; set; } + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ManagedSettingsDiagnosticSeverity left, ManagedSettingsDiagnosticSeverity right) => !(left == right); - /// Whether uninstall restores a protected pre-install configuration. - [JsonPropertyName("restoresPreviousConfiguration")] - public required bool RestoresPreviousConfiguration { get; set; } + /// + public override bool Equals(object? obj) => obj is ManagedSettingsDiagnosticSeverity other && Equals(other); - /// Exact destination, checked for intervening changes before mutation. - [JsonPropertyName("target")] - public required McpPlanTarget Target { get; set; } -} + /// + public bool Equals(ManagedSettingsDiagnosticSeverity other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); -/// The mcp variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class InstallationReviewMcp : InstallationReview -{ /// - [JsonIgnore] - public override string Resource => "mcp"; + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// The exact MCP action and its reviewed changes. - [JsonPropertyName("review")] - public required McpInstallationReview Review { get; set; } + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ManagedSettingsDiagnosticSeverity Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ManagedSettingsDiagnosticSeverity value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ManagedSettingsDiagnosticSeverity)); + } + } } -/// The skill variant of . + +/// A channel accepted by managedSettings.compose. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class InstallationReviewSkill : InstallationReview +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ManagedSettingsChannel : IEquatable { - /// - [JsonIgnore] - public override string Resource => "skill"; + private readonly string? _value; - /// The exact verified Skill action and its reviewed files. - [JsonPropertyName("review")] - public required SkillInstallationReview Review { get; set; } -} + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ManagedSettingsChannel(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } -/// One connection-owned, expiring request for a trusted host's explicit user decision. -public sealed class InstallationsConfirmRequest -{ - /// Opaque one-use challenge. Return unchanged; never log or persist. - [JsonPropertyName("confirmationId")] - public string ConfirmationId { get; set; } = string.Empty; + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// Original plan expiry as an ISO 8601 timestamp. Confirmation never extends it. - [JsonPropertyName("expiresAt")] - public string ExpiresAt { get; set; } = string.Empty; + /// Device policy, the strongest channel. + public static ManagedSettingsChannel Device { get; } = new("device"); - /// Random identifier of this installation operation, not a plan handle. - [JsonPropertyName("operationId")] - public string OperationId { get; set; } = string.Empty; + /// Account or organization policy. + public static ManagedSettingsChannel Server { get; } = new("server"); - /// - /// Original engine-resolved selector for a bound operation, never a dispatch default. - /// Bound MCP confirmation always includes it; correlate it with the original pending action. - /// - [JsonPropertyName("policySessionId")] - public string? PolicySessionId { get; set; } + /// Session-local helper output, the weakest channel. + public static ManagedSettingsChannel PolicyHelper { get; } = new("policyHelper"); - /// Resource-specific review to present before collecting the user's decision. - [JsonPropertyName("review")] - public InstallationReview Review { get => field ??= new(); set; } + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ManagedSettingsChannel left, ManagedSettingsChannel right) => left.Equals(right); - /// Opaque commitment to the exact review and inputs. Return unchanged; never log. - [JsonPropertyName("reviewFingerprint")] - public string ReviewFingerprint { get; set; } = string.Empty; + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ManagedSettingsChannel left, ManagedSettingsChannel right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is ManagedSettingsChannel other && Equals(other); + + /// + public bool Equals(ManagedSettingsChannel other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ManagedSettingsChannel Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ManagedSettingsChannel value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ManagedSettingsChannel)); + } + } } -/// Closed set of public task kinds a connection can negotiate. + +/// Managed-policy verdict only: deny blocks the URL, ask requires approval, allow approves under managed policy, and unmanaged leaves the normal consumer permission flow in effect. No verdict bypasses other security controls. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct TaskKind : IEquatable +public readonly struct ManagedPermissionVerdict : IEquatable { private readonly string? _value; - /// Initializes a new instance of the struct. - /// The value to associate with this . + /// Initializes a new instance of the struct. + /// The value to associate with this . [JsonConstructor] - public TaskKind(string value) + public ManagedPermissionVerdict(string value) { ArgumentException.ThrowIfNullOrWhiteSpace(value); _value = value; } - /// Gets the value associated with this . + /// Gets the value associated with this . public string Value => _value ?? string.Empty; - /// Runtime-owned background agent task. - public static TaskKind Agent { get; } = new("agent"); + /// Managed policy prohibits the operation; do not offer approval. + public static ManagedPermissionVerdict Deny { get; } = new("deny"); - /// Runtime-owned shell task. - public static TaskKind Shell { get; } = new("shell"); + /// Managed policy requires the consumer to obtain approval. + public static ManagedPermissionVerdict Ask { get; } = new("ask"); - /// Client-owned externally executed task. - public static TaskKind Client { get; } = new("client"); + /// Managed policy allows the operation, subject to other security controls. + public static ManagedPermissionVerdict Allow { get; } = new("allow"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(TaskKind left, TaskKind right) => left.Equals(right); + /// Managed policy does not decide the operation; use the consumer's normal permission flow. + public static ManagedPermissionVerdict Unmanaged { get; } = new("unmanaged"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(TaskKind left, TaskKind right) => !(left == right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ManagedPermissionVerdict left, ManagedPermissionVerdict right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ManagedPermissionVerdict left, ManagedPermissionVerdict right) => !(left == right); /// - public override bool Equals(object? obj) => obj is TaskKind other && Equals(other); + public override bool Equals(object? obj) => obj is ManagedPermissionVerdict other && Equals(other); /// - public bool Equals(TaskKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ManagedPermissionVerdict other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -27963,20 +31377,20 @@ public TaskKind(string value) /// public override string ToString() => Value; - /// Provides a for serializing instances. + /// Provides a for serializing instances. [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter + public sealed class Converter : JsonConverter { /// - public override TaskKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ManagedPermissionVerdict Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, TaskKind value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ManagedPermissionVerdict value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskKind)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ManagedPermissionVerdict)); } } } @@ -28501,49 +31915,64 @@ public override void Write(Utf8JsonWriter writer, ModelPolicyState value, JsonSe } -/// State of the persistent certificate authority of the sandbox credential proxy. +/// Authentication type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct SandboxProxyCaState : IEquatable +public readonly struct AuthInfoType : IEquatable { private readonly string? _value; - /// Initializes a new instance of the struct. - /// The value to associate with this . + /// Initializes a new instance of the struct. + /// The value to associate with this . [JsonConstructor] - public SandboxProxyCaState(string value) + public AuthInfoType(string value) { ArgumentException.ThrowIfNullOrWhiteSpace(value); _value = value; } - /// Gets the value associated with this . + /// Gets the value associated with this . public string Value => _value ?? string.Empty; - /// This platform has no supported OS trust store. The proxy uses a per-process certificate bundle. - public static SandboxProxyCaState Unsupported { get; } = new("unsupported"); + /// Authentication provided by a GitHub App HMAC credential. + public static AuthInfoType Hmac { get; } = new("hmac"); - /// OS trust does not include the certificate authority, or none is stored. - public static SandboxProxyCaState NotInstalled { get; } = new("notInstalled"); + /// Authentication resolved from environment-provided credentials. + public static AuthInfoType Env { get; } = new("env"); - /// OS trust includes the stored certificate authority. - public static SandboxProxyCaState Installed { get; } = new("installed"); + /// Authentication from an interactive user sign-in. + public static AuthInfoType User { get; } = new("user"); - /// The runtime could not read the certificate authority or the OS trust store. - public static SandboxProxyCaState Error { get; } = new("error"); + /// Authentication from a selected provider-owned account, without a GitHub credential. + public static AuthInfoType Account { get; } = new("account"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SandboxProxyCaState left, SandboxProxyCaState right) => left.Equals(right); + /// Authentication delegated to the GitHub CLI. + public static AuthInfoType GhCli { get; } = new("gh-cli"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SandboxProxyCaState left, SandboxProxyCaState right) => !(left == right); + /// Authentication from an API key credential. + public static AuthInfoType ApiKey { get; } = new("api-key"); + + /// Authentication from a GitHub token. + public static AuthInfoType Token { get; } = new("token"); + + /// Authentication from an SDK GitHub token callback. + public static AuthInfoType TokenProvider { get; } = new("token-provider"); + + /// Authentication from a Copilot API token. + public static AuthInfoType CopilotApiToken { get; } = new("copilot-api-token"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(AuthInfoType left, AuthInfoType right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(AuthInfoType left, AuthInfoType right) => !(left == right); /// - public override bool Equals(object? obj) => obj is SandboxProxyCaState other && Equals(other); + public override bool Equals(object? obj) => obj is AuthInfoType other && Equals(other); /// - public bool Equals(SandboxProxyCaState other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(AuthInfoType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -28551,20 +31980,20 @@ public SandboxProxyCaState(string value) /// public override string ToString() => Value; - /// Provides a for serializing instances. + /// Provides a for serializing instances. [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter + public sealed class Converter : JsonConverter { /// - public override SandboxProxyCaState Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override AuthInfoType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, SandboxProxyCaState value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, AuthInfoType value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SandboxProxyCaState)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AuthInfoType)); } } } @@ -28639,6 +32068,69 @@ public override void Write(Utf8JsonWriter writer, DiscoveredMcpServerType value, } +/// Controls how availableTools (allowlist) and excludedTools (denylist) combine when both are set. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct OptionsUpdateToolFilterPrecedence : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public OptionsUpdateToolFilterPrecedence(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// If availableTools is set, it is the only constraint that applies (excludedTools is ignored). Preserves CLI / pre-existing client behavior. Default. + public static OptionsUpdateToolFilterPrecedence Available { get; } = new("available"); + + /// A tool is enabled if and only if it matches the allowlist (or the allowlist is unset) AND it does not match the denylist. Makes 'all except X' expressible by combining the two lists. + public static OptionsUpdateToolFilterPrecedence Excluded { get; } = new("excluded"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(OptionsUpdateToolFilterPrecedence left, OptionsUpdateToolFilterPrecedence right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(OptionsUpdateToolFilterPrecedence left, OptionsUpdateToolFilterPrecedence right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is OptionsUpdateToolFilterPrecedence other && Equals(other); + + /// + public bool Equals(OptionsUpdateToolFilterPrecedence other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override OptionsUpdateToolFilterPrecedence Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, OptionsUpdateToolFilterPrecedence value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(OptionsUpdateToolFilterPrecedence)); + } + } +} + + /// A wire feature a caller can require of the catalog surface, negotiated per request. A grant means the runtime understands the feature's contract, not that the deployment has enabled the operation; typed unavailable results report availability separately. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] @@ -31033,6 +34525,285 @@ public override void Write(Utf8JsonWriter writer, DiscoveredExtensionMode value, } +/// Where this source lives — used for UI grouping. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct InstructionSourceLocation : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public InstructionSourceLocation(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Instructions live in user-level configuration. + public static InstructionSourceLocation User { get; } = new("user"); + + /// Instructions live in repository-level configuration. + public static InstructionSourceLocation Repository { get; } = new("repository"); + + /// Instructions live under the current working directory. + public static InstructionSourceLocation WorkingDirectory { get; } = new("working-directory"); + + /// Instructions live in plugin-provided configuration. + public static InstructionSourceLocation Plugin { get; } = new("plugin"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(InstructionSourceLocation left, InstructionSourceLocation right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(InstructionSourceLocation left, InstructionSourceLocation right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is InstructionSourceLocation other && Equals(other); + + /// + public bool Equals(InstructionSourceLocation other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override InstructionSourceLocation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, InstructionSourceLocation value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(InstructionSourceLocation)); + } + } +} + + +/// Category of instruction source — used for merge logic. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct InstructionSourceType : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public InstructionSourceType(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Instructions loaded from the user's home configuration. + public static InstructionSourceType Home { get; } = new("home"); + + /// Instructions loaded from repository-scoped files. + public static InstructionSourceType Repo { get; } = new("repo"); + + /// Instructions loaded from model-specific files. + public static InstructionSourceType Model { get; } = new("model"); + + /// Instructions loaded from VS Code instruction files. + public static InstructionSourceType Vscode { get; } = new("vscode"); + + /// Instructions discovered from nested agent files. + public static InstructionSourceType NestedAgents { get; } = new("nested-agents"); + + /// Instructions inherited from child instruction files. + public static InstructionSourceType ChildInstructions { get; } = new("child-instructions"); + + /// Instructions supplied by an installed plugin. + public static InstructionSourceType Plugin { get; } = new("plugin"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(InstructionSourceType left, InstructionSourceType right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(InstructionSourceType left, InstructionSourceType right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is InstructionSourceType other && Equals(other); + + /// + public bool Equals(InstructionSourceType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override InstructionSourceType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, InstructionSourceType value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(InstructionSourceType)); + } + } +} + + +/// Whether the target is a single file or a directory of instruction files. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct InstructionDiscoveryPathKind : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public InstructionDiscoveryPathKind(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The target is a single instruction file. + public static InstructionDiscoveryPathKind File { get; } = new("file"); + + /// The target is a directory that holds instruction files. + public static InstructionDiscoveryPathKind Directory { get; } = new("directory"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(InstructionDiscoveryPathKind left, InstructionDiscoveryPathKind right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(InstructionDiscoveryPathKind left, InstructionDiscoveryPathKind right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is InstructionDiscoveryPathKind other && Equals(other); + + /// + public bool Equals(InstructionDiscoveryPathKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override InstructionDiscoveryPathKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, InstructionDiscoveryPathKind value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(InstructionDiscoveryPathKind)); + } + } +} + + +/// Which tier this target belongs to. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct InstructionDiscoveryPathLocation : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public InstructionDiscoveryPathLocation(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Instructions live in user-level configuration. + public static InstructionDiscoveryPathLocation User { get; } = new("user"); + + /// Instructions live in repository-level configuration. + public static InstructionDiscoveryPathLocation Repository { get; } = new("repository"); + + /// Instructions live under the current working directory. + public static InstructionDiscoveryPathLocation WorkingDirectory { get; } = new("working-directory"); + + /// Instructions live in plugin-provided configuration. + public static InstructionDiscoveryPathLocation Plugin { get; } = new("plugin"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(InstructionDiscoveryPathLocation left, InstructionDiscoveryPathLocation right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(InstructionDiscoveryPathLocation left, InstructionDiscoveryPathLocation right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is InstructionDiscoveryPathLocation other && Equals(other); + + /// + public bool Equals(InstructionDiscoveryPathLocation other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override InstructionDiscoveryPathLocation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, InstructionDiscoveryPathLocation value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(InstructionDiscoveryPathLocation)); + } + } +} + + /// Defines the allowed values. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] @@ -32161,285 +35932,6 @@ public override void Write(Utf8JsonWriter writer, AgentDiscoveryPathScope value, } -/// Where this source lives — used for UI grouping. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct InstructionSourceLocation : IEquatable -{ - private readonly string? _value; - - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public InstructionSourceLocation(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// Instructions live in user-level configuration. - public static InstructionSourceLocation User { get; } = new("user"); - - /// Instructions live in repository-level configuration. - public static InstructionSourceLocation Repository { get; } = new("repository"); - - /// Instructions live under the current working directory. - public static InstructionSourceLocation WorkingDirectory { get; } = new("working-directory"); - - /// Instructions live in plugin-provided configuration. - public static InstructionSourceLocation Plugin { get; } = new("plugin"); - - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(InstructionSourceLocation left, InstructionSourceLocation right) => left.Equals(right); - - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(InstructionSourceLocation left, InstructionSourceLocation right) => !(left == right); - - /// - public override bool Equals(object? obj) => obj is InstructionSourceLocation other && Equals(other); - - /// - public bool Equals(InstructionSourceLocation other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - - /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - - /// - public override string ToString() => Value; - - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter - { - /// - public override InstructionSourceLocation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, InstructionSourceLocation value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(InstructionSourceLocation)); - } - } -} - - -/// Category of instruction source — used for merge logic. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct InstructionSourceType : IEquatable -{ - private readonly string? _value; - - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public InstructionSourceType(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// Instructions loaded from the user's home configuration. - public static InstructionSourceType Home { get; } = new("home"); - - /// Instructions loaded from repository-scoped files. - public static InstructionSourceType Repo { get; } = new("repo"); - - /// Instructions loaded from model-specific files. - public static InstructionSourceType Model { get; } = new("model"); - - /// Instructions loaded from VS Code instruction files. - public static InstructionSourceType Vscode { get; } = new("vscode"); - - /// Instructions discovered from nested agent files. - public static InstructionSourceType NestedAgents { get; } = new("nested-agents"); - - /// Instructions inherited from child instruction files. - public static InstructionSourceType ChildInstructions { get; } = new("child-instructions"); - - /// Instructions supplied by an installed plugin. - public static InstructionSourceType Plugin { get; } = new("plugin"); - - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(InstructionSourceType left, InstructionSourceType right) => left.Equals(right); - - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(InstructionSourceType left, InstructionSourceType right) => !(left == right); - - /// - public override bool Equals(object? obj) => obj is InstructionSourceType other && Equals(other); - - /// - public bool Equals(InstructionSourceType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - - /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - - /// - public override string ToString() => Value; - - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter - { - /// - public override InstructionSourceType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, InstructionSourceType value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(InstructionSourceType)); - } - } -} - - -/// Whether the target is a single file or a directory of instruction files. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct InstructionDiscoveryPathKind : IEquatable -{ - private readonly string? _value; - - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public InstructionDiscoveryPathKind(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// The target is a single instruction file. - public static InstructionDiscoveryPathKind File { get; } = new("file"); - - /// The target is a directory that holds instruction files. - public static InstructionDiscoveryPathKind Directory { get; } = new("directory"); - - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(InstructionDiscoveryPathKind left, InstructionDiscoveryPathKind right) => left.Equals(right); - - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(InstructionDiscoveryPathKind left, InstructionDiscoveryPathKind right) => !(left == right); - - /// - public override bool Equals(object? obj) => obj is InstructionDiscoveryPathKind other && Equals(other); - - /// - public bool Equals(InstructionDiscoveryPathKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - - /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - - /// - public override string ToString() => Value; - - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter - { - /// - public override InstructionDiscoveryPathKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, InstructionDiscoveryPathKind value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(InstructionDiscoveryPathKind)); - } - } -} - - -/// Which tier this target belongs to. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct InstructionDiscoveryPathLocation : IEquatable -{ - private readonly string? _value; - - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public InstructionDiscoveryPathLocation(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// Instructions live in user-level configuration. - public static InstructionDiscoveryPathLocation User { get; } = new("user"); - - /// Instructions live in repository-level configuration. - public static InstructionDiscoveryPathLocation Repository { get; } = new("repository"); - - /// Instructions live under the current working directory. - public static InstructionDiscoveryPathLocation WorkingDirectory { get; } = new("working-directory"); - - /// Instructions live in plugin-provided configuration. - public static InstructionDiscoveryPathLocation Plugin { get; } = new("plugin"); - - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(InstructionDiscoveryPathLocation left, InstructionDiscoveryPathLocation right) => left.Equals(right); - - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(InstructionDiscoveryPathLocation left, InstructionDiscoveryPathLocation right) => !(left == right); - - /// - public override bool Equals(object? obj) => obj is InstructionDiscoveryPathLocation other && Equals(other); - - /// - public bool Equals(InstructionDiscoveryPathLocation other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - - /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - - /// - public override string ToString() => Value; - - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter - { - /// - public override InstructionDiscoveryPathLocation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, InstructionDiscoveryPathLocation value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(InstructionDiscoveryPathLocation)); - } - } -} - - /// Optional completion hint for the input (e.g. 'directory' for filesystem path completion). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] @@ -32629,135 +36121,6 @@ public override void Write(Utf8JsonWriter writer, SessionWorkingDirectoryContext } -/// Severity of a managed-settings validation finding. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct ManagedSettingsDiagnosticSeverity : IEquatable -{ - private readonly string? _value; - - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public ManagedSettingsDiagnosticSeverity(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// The runtime rejects the document. - public static ManagedSettingsDiagnosticSeverity Error { get; } = new("error"); - - /// The runtime accepts the document but ignores the flagged content. - public static ManagedSettingsDiagnosticSeverity Warning { get; } = new("warning"); - - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ManagedSettingsDiagnosticSeverity left, ManagedSettingsDiagnosticSeverity right) => left.Equals(right); - - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ManagedSettingsDiagnosticSeverity left, ManagedSettingsDiagnosticSeverity right) => !(left == right); - - /// - public override bool Equals(object? obj) => obj is ManagedSettingsDiagnosticSeverity other && Equals(other); - - /// - public bool Equals(ManagedSettingsDiagnosticSeverity other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - - /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - - /// - public override string ToString() => Value; - - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter - { - /// - public override ManagedSettingsDiagnosticSeverity Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, ManagedSettingsDiagnosticSeverity value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ManagedSettingsDiagnosticSeverity)); - } - } -} - - -/// A channel accepted by managedSettings.compose. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct ManagedSettingsChannel : IEquatable -{ - private readonly string? _value; - - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public ManagedSettingsChannel(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// Device policy, the strongest channel. - public static ManagedSettingsChannel Device { get; } = new("device"); - - /// Account or organization policy. - public static ManagedSettingsChannel Server { get; } = new("server"); - - /// Session-local helper output, the weakest channel. - public static ManagedSettingsChannel PolicyHelper { get; } = new("policyHelper"); - - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ManagedSettingsChannel left, ManagedSettingsChannel right) => left.Equals(right); - - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ManagedSettingsChannel left, ManagedSettingsChannel right) => !(left == right); - - /// - public override bool Equals(object? obj) => obj is ManagedSettingsChannel other && Equals(other); - - /// - public bool Equals(ManagedSettingsChannel other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - - /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - - /// - public override string ToString() => Value; - - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter - { - /// - public override ManagedSettingsChannel Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, ManagedSettingsChannel value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ManagedSettingsChannel)); - } - } -} - - /// Path conventions used by this filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] @@ -34090,90 +37453,6 @@ public override void Write(Utf8JsonWriter writer, ConnectorDiscoveryAvailability } -/// Authentication type. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct AuthInfoType : IEquatable -{ - private readonly string? _value; - - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public AuthInfoType(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// Authentication provided by a GitHub App HMAC credential. - public static AuthInfoType Hmac { get; } = new("hmac"); - - /// Authentication resolved from environment-provided credentials. - public static AuthInfoType Env { get; } = new("env"); - - /// Authentication from an interactive user sign-in. - public static AuthInfoType User { get; } = new("user"); - - /// Authentication from a selected provider-owned account, without a GitHub credential. - public static AuthInfoType Account { get; } = new("account"); - - /// Authentication delegated to the GitHub CLI. - public static AuthInfoType GhCli { get; } = new("gh-cli"); - - /// Authentication from an API key credential. - public static AuthInfoType ApiKey { get; } = new("api-key"); - - /// Authentication from a GitHub token. - public static AuthInfoType Token { get; } = new("token"); - - /// Authentication from an SDK GitHub token callback. - public static AuthInfoType TokenProvider { get; } = new("token-provider"); - - /// Authentication from a Copilot API token. - public static AuthInfoType CopilotApiToken { get; } = new("copilot-api-token"); - - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(AuthInfoType left, AuthInfoType right) => left.Equals(right); - - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(AuthInfoType left, AuthInfoType right) => !(left == right); - - /// - public override bool Equals(object? obj) => obj is AuthInfoType other && Equals(other); - - /// - public bool Equals(AuthInfoType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - - /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - - /// - public override string ToString() => Value; - - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter - { - /// - public override AuthInfoType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, AuthInfoType value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AuthInfoType)); - } - } -} - - /// Authoritative service connection state for one Connector. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] @@ -39292,6 +42571,81 @@ public override void Write(Utf8JsonWriter writer, ConnectorMcpStatus value, Json } +/// What retrying did for one plugin required by managed settings. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ManagedPluginRetryStatus : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ManagedPluginRetryStatus(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The plugin was missing and is now installed. + public static ManagedPluginRetryStatus Installed { get; } = new("installed"); + + /// An update was available and is now installed. + public static ManagedPluginRetryStatus Updated { get; } = new("updated"); + + /// The plugin was already installed and current, so nothing was done. + public static ManagedPluginRetryStatus AlreadyPresent { get; } = new("already_present"); + + /// Checking, installing, or updating the plugin failed. See `error`. + public static ManagedPluginRetryStatus Failed { get; } = new("failed"); + + /// The plugin was not prepared because another session was changing plugins or the managed settings changed during the retry. It is prepared again before a later message. + public static ManagedPluginRetryStatus Deferred { get; } = new("deferred"); + + /// The session's managed settings do not require this plugin, so nothing was done. + public static ManagedPluginRetryStatus NotRequired { get; } = new("not_required"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ManagedPluginRetryStatus left, ManagedPluginRetryStatus right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ManagedPluginRetryStatus left, ManagedPluginRetryStatus right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is ManagedPluginRetryStatus other && Equals(other); + + /// + public bool Equals(ManagedPluginRetryStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ManagedPluginRetryStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ManagedPluginRetryStatus value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ManagedPluginRetryStatus)); + } + } +} + + /// Allowed values for the `OptionsUpdateAdditionalContentExclusionPolicyScope` enumeration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] @@ -39841,69 +43195,6 @@ public override void Write(Utf8JsonWriter writer, ShellInitScriptShell value, Js } -/// Controls how availableTools (allowlist) and excludedTools (denylist) combine when both are set. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct OptionsUpdateToolFilterPrecedence : IEquatable -{ - private readonly string? _value; - - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public OptionsUpdateToolFilterPrecedence(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// If availableTools is set, it is the only constraint that applies (excludedTools is ignored). Preserves CLI / pre-existing client behavior. Default. - public static OptionsUpdateToolFilterPrecedence Available { get; } = new("available"); - - /// A tool is enabled if and only if it matches the allowlist (or the allowlist is unset) AND it does not match the denylist. Makes 'all except X' expressible by combining the two lists. - public static OptionsUpdateToolFilterPrecedence Excluded { get; } = new("excluded"); - - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(OptionsUpdateToolFilterPrecedence left, OptionsUpdateToolFilterPrecedence right) => left.Equals(right); - - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(OptionsUpdateToolFilterPrecedence left, OptionsUpdateToolFilterPrecedence right) => !(left == right); - - /// - public override bool Equals(object? obj) => obj is OptionsUpdateToolFilterPrecedence other && Equals(other); - - /// - public bool Equals(OptionsUpdateToolFilterPrecedence other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - - /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - - /// - public override string ToString() => Value; - - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter - { - /// - public override OptionsUpdateToolFilterPrecedence Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, OptionsUpdateToolFilterPrecedence value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(OptionsUpdateToolFilterPrecedence)); - } - } -} - - /// Discovery source: project (.github/extensions/), user (~/.copilot/extensions/), plugin (installed plugin), or session (session-state/<id>/extensions/). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] @@ -42476,26 +45767,473 @@ public SessionLimitPredictionSource(string value) /// Gets the value associated with this . public string Value => _value ?? string.Empty; - /// The prediction used the exact resolved model's baseline cell. - public static SessionLimitPredictionSource Model { get; } = new("model"); + /// The prediction used the exact resolved model's baseline cell. + public static SessionLimitPredictionSource Model { get; } = new("model"); + + /// The exact model was unavailable, so the prediction used the model family's baseline cell. + public static SessionLimitPredictionSource Family { get; } = new("family"); + + /// No model or family cell was available, so the prediction used the global client-type baseline cell. + public static SessionLimitPredictionSource Global { get; } = new("global"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SessionLimitPredictionSource left, SessionLimitPredictionSource right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SessionLimitPredictionSource left, SessionLimitPredictionSource right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is SessionLimitPredictionSource other && Equals(other); + + /// + public bool Equals(SessionLimitPredictionSource other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override SessionLimitPredictionSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, SessionLimitPredictionSource value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionLimitPredictionSource)); + } + } +} + + +/// Reason a prediction could not be computed. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct SessionLimitPredictionUnavailableReason : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public SessionLimitPredictionUnavailableReason(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The current model is auto and has not resolved to a concrete model yet. + public static SessionLimitPredictionUnavailableReason AutoUnresolved { get; } = new("auto_unresolved"); + + /// No model was provided and the session does not currently have a selected model. + public static SessionLimitPredictionUnavailableReason NoModel { get; } = new("no_model"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SessionLimitPredictionUnavailableReason left, SessionLimitPredictionUnavailableReason right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SessionLimitPredictionUnavailableReason left, SessionLimitPredictionUnavailableReason right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is SessionLimitPredictionUnavailableReason other && Equals(other); + + /// + public bool Equals(SessionLimitPredictionUnavailableReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override SessionLimitPredictionUnavailableReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, SessionLimitPredictionUnavailableReason value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionLimitPredictionUnavailableReason)); + } + } +} + + +/// Per-session remote mode. "off" disables remote, "export" exports session events to GitHub without enabling remote steering, "on" enables both export and remote steering. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct RemoteSessionMode : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public RemoteSessionMode(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Disable remote session export and steering. + public static RemoteSessionMode Off { get; } = new("off"); + + /// Export session events to GitHub without enabling remote steering. + public static RemoteSessionMode Export { get; } = new("export"); + + /// Enable both remote session export and remote steering. + public static RemoteSessionMode On { get; } = new("on"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(RemoteSessionMode left, RemoteSessionMode right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(RemoteSessionMode left, RemoteSessionMode right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is RemoteSessionMode other && Equals(other); + + /// + public bool Equals(RemoteSessionMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override RemoteSessionMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, RemoteSessionMode value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(RemoteSessionMode)); + } + } +} + + +/// Sharing status for a synced session. "repo" makes the session visible to anyone with read access to the repository; "unshared" restricts it to the creator and collaborators. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct SessionVisibilityStatus : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public SessionVisibilityStatus(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The session is visible to repository readers. + public static SessionVisibilityStatus Repo { get; } = new("repo"); + + /// The session is restricted to its creator and collaborators. + public static SessionVisibilityStatus Unshared { get; } = new("unshared"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SessionVisibilityStatus left, SessionVisibilityStatus right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SessionVisibilityStatus left, SessionVisibilityStatus right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is SessionVisibilityStatus other && Equals(other); + + /// + public bool Equals(SessionVisibilityStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override SessionVisibilityStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, SessionVisibilityStatus value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionVisibilityStatus)); + } + } +} + + +/// Why the runtime requests client-task cancellation. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ClientTaskCancelReason : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ClientTaskCancelReason(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// A caller requested task cancellation. + public static ClientTaskCancelReason CancelRequested { get; } = new("cancel_requested"); + + /// The session is shutting down. + public static ClientTaskCancelReason SessionShutdown { get; } = new("session_shutdown"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ClientTaskCancelReason left, ClientTaskCancelReason right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ClientTaskCancelReason left, ClientTaskCancelReason right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is ClientTaskCancelReason other && Equals(other); + + /// + public bool Equals(ClientTaskCancelReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ClientTaskCancelReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ClientTaskCancelReason value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ClientTaskCancelReason)); + } + } +} + + +/// Error classification. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct SessionFsErrorCode : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public SessionFsErrorCode(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The requested path does not exist. + public static SessionFsErrorCode ENOENT { get; } = new("ENOENT"); + + /// The filesystem operation failed for an unspecified reason. + public static SessionFsErrorCode UNKNOWN { get; } = new("UNKNOWN"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SessionFsErrorCode left, SessionFsErrorCode right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SessionFsErrorCode left, SessionFsErrorCode right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is SessionFsErrorCode other && Equals(other); + + /// + public bool Equals(SessionFsErrorCode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override SessionFsErrorCode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, SessionFsErrorCode value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionFsErrorCode)); + } + } +} + + +/// Entry type. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct SessionFsReaddirWithTypesEntryType : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public SessionFsReaddirWithTypesEntryType(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The entry is a file. + public static SessionFsReaddirWithTypesEntryType File { get; } = new("file"); + + /// The entry is a directory. + public static SessionFsReaddirWithTypesEntryType Directory { get; } = new("directory"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SessionFsReaddirWithTypesEntryType left, SessionFsReaddirWithTypesEntryType right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SessionFsReaddirWithTypesEntryType left, SessionFsReaddirWithTypesEntryType right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is SessionFsReaddirWithTypesEntryType other && Equals(other); + + /// + public bool Equals(SessionFsReaddirWithTypesEntryType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override SessionFsReaddirWithTypesEntryType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, SessionFsReaddirWithTypesEntryType value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionFsReaddirWithTypesEntryType)); + } + } +} + + +/// How to execute the query: 'exec' for DDL/multi-statement (no results), 'query' for SELECT (returns rows), 'run' for INSERT/UPDATE/DELETE (returns rowsAffected). +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct SessionFsSqliteQueryType : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public SessionFsSqliteQueryType(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Execute DDL or multi-statement SQL without returning rows. + public static SessionFsSqliteQueryType Exec { get; } = new("exec"); - /// The exact model was unavailable, so the prediction used the model family's baseline cell. - public static SessionLimitPredictionSource Family { get; } = new("family"); + /// Execute a SELECT-style query and return rows. + public static SessionFsSqliteQueryType Query { get; } = new("query"); - /// No model or family cell was available, so the prediction used the global client-type baseline cell. - public static SessionLimitPredictionSource Global { get; } = new("global"); + /// Execute INSERT, UPDATE, or DELETE SQL and return affected-row metadata. + public static SessionFsSqliteQueryType Run { get; } = new("run"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SessionLimitPredictionSource left, SessionLimitPredictionSource right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SessionFsSqliteQueryType left, SessionFsSqliteQueryType right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SessionLimitPredictionSource left, SessionLimitPredictionSource right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SessionFsSqliteQueryType left, SessionFsSqliteQueryType right) => !(left == right); /// - public override bool Equals(object? obj) => obj is SessionLimitPredictionSource other && Equals(other); + public override bool Equals(object? obj) => obj is SessionFsSqliteQueryType other && Equals(other); /// - public bool Equals(SessionLimitPredictionSource other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(SessionFsSqliteQueryType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -42503,62 +46241,65 @@ public SessionLimitPredictionSource(string value) /// public override string ToString() => Value; - /// Provides a for serializing instances. + /// Provides a for serializing instances. [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter + public sealed class Converter : JsonConverter { /// - public override SessionLimitPredictionSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override SessionFsSqliteQueryType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, SessionLimitPredictionSource value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, SessionFsSqliteQueryType value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionLimitPredictionSource)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionFsSqliteQueryType)); } } } -/// Reason a prediction could not be computed. +/// SQLite transaction failure classification. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct SessionLimitPredictionUnavailableReason : IEquatable +public readonly struct SessionFsSqliteTransactionErrorClass : IEquatable { private readonly string? _value; - /// Initializes a new instance of the struct. - /// The value to associate with this . + /// Initializes a new instance of the struct. + /// The value to associate with this . [JsonConstructor] - public SessionLimitPredictionUnavailableReason(string value) + public SessionFsSqliteTransactionErrorClass(string value) { ArgumentException.ThrowIfNullOrWhiteSpace(value); _value = value; } - /// Gets the value associated with this . + /// Gets the value associated with this . public string Value => _value ?? string.Empty; - /// The current model is auto and has not resolved to a concrete model yet. - public static SessionLimitPredictionUnavailableReason AutoUnresolved { get; } = new("auto_unresolved"); + /// SQLite reported BUSY or LOCKED before commit; the transaction was rolled back and may be retried. + public static SessionFsSqliteTransactionErrorClass BusyOrLocked { get; } = new("busyOrLocked"); - /// No model was provided and the session does not currently have a selected model. - public static SessionLimitPredictionUnavailableReason NoModel { get; } = new("no_model"); + /// The statement, database, or provider failed definitively and must not be retried automatically. + public static SessionFsSqliteTransactionErrorClass Fatal { get; } = new("fatal"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SessionLimitPredictionUnavailableReason left, SessionLimitPredictionUnavailableReason right) => left.Equals(right); + /// The transport failed after the provider may have committed; retrying could duplicate effects. + public static SessionFsSqliteTransactionErrorClass PostCommitAmbiguous { get; } = new("postCommitAmbiguous"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SessionLimitPredictionUnavailableReason left, SessionLimitPredictionUnavailableReason right) => !(left == right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SessionFsSqliteTransactionErrorClass left, SessionFsSqliteTransactionErrorClass right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SessionFsSqliteTransactionErrorClass left, SessionFsSqliteTransactionErrorClass right) => !(left == right); /// - public override bool Equals(object? obj) => obj is SessionLimitPredictionUnavailableReason other && Equals(other); + public override bool Equals(object? obj) => obj is SessionFsSqliteTransactionErrorClass other && Equals(other); /// - public bool Equals(SessionLimitPredictionUnavailableReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(SessionFsSqliteTransactionErrorClass other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -42566,65 +46307,68 @@ public SessionLimitPredictionUnavailableReason(string value) /// public override string ToString() => Value; - /// Provides a for serializing instances. + /// Provides a for serializing instances. [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter + public sealed class Converter : JsonConverter { /// - public override SessionLimitPredictionUnavailableReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override SessionFsSqliteTransactionErrorClass Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, SessionLimitPredictionUnavailableReason value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, SessionFsSqliteTransactionErrorClass value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionLimitPredictionUnavailableReason)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionFsSqliteTransactionErrorClass)); } } } -/// Per-session remote mode. "off" disables remote, "export" exports session events to GitHub without enabling remote steering, "on" enables both export and remote steering. +/// Defines the allowed values. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct RemoteSessionMode : IEquatable +public readonly struct HostExitReason : IEquatable { private readonly string? _value; - /// Initializes a new instance of the struct. - /// The value to associate with this . + /// Initializes a new instance of the struct. + /// The value to associate with this . [JsonConstructor] - public RemoteSessionMode(string value) + public HostExitReason(string value) { ArgumentException.ThrowIfNullOrWhiteSpace(value); _value = value; } - /// Gets the value associated with this . + /// Gets the value associated with this . public string Value => _value ?? string.Empty; - /// Disable remote session export and steering. - public static RemoteSessionMode Off { get; } = new("off"); + /// The owner requested disposal. + public static HostExitReason Disposed { get; } = new("disposed"); - /// Export session events to GitHub without enabling remote steering. - public static RemoteSessionMode Export { get; } = new("export"); + /// The hosting task or its SDK transport exited; this does not mean the runtime process exited. + public static HostExitReason Exited { get; } = new("exited"); - /// Enable both remote session export and remote steering. - public static RemoteSessionMode On { get; } = new("on"); + /// The owning SDK connection disconnected. + public static HostExitReason OwnerDisconnected { get; } = new("ownerDisconnected"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(RemoteSessionMode left, RemoteSessionMode right) => left.Equals(right); + /// The runtime is shutting down. + public static HostExitReason RuntimeShutdown { get; } = new("runtimeShutdown"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(RemoteSessionMode left, RemoteSessionMode right) => !(left == right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(HostExitReason left, HostExitReason right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(HostExitReason left, HostExitReason right) => !(left == right); /// - public override bool Equals(object? obj) => obj is RemoteSessionMode other && Equals(other); + public override bool Equals(object? obj) => obj is HostExitReason other && Equals(other); /// - public bool Equals(RemoteSessionMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(HostExitReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -42632,62 +46376,62 @@ public RemoteSessionMode(string value) /// public override string ToString() => Value; - /// Provides a for serializing instances. + /// Provides a for serializing instances. [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter + public sealed class Converter : JsonConverter { /// - public override RemoteSessionMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override HostExitReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, RemoteSessionMode value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, HostExitReason value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(RemoteSessionMode)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(HostExitReason)); } } } -/// Sharing status for a synced session. "repo" makes the session visible to anyone with read access to the repository; "unshared" restricts it to the creator and collaborators. +/// Transport the runtime would otherwise use for this request. `http` (the default when absent) covers plain HTTP and SSE responses; `websocket` indicates a full-duplex message channel where each body chunk maps to one WebSocket message and the `binary` flag distinguishes text from binary frames. The SDK consumer uses this to decide whether to service the request with an HTTP client or a WebSocket client. It is the one piece of request metadata the consumer cannot reliably infer from the URL or headers alone. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct SessionVisibilityStatus : IEquatable +public readonly struct LlmInferenceHttpRequestStartTransport : IEquatable { private readonly string? _value; - /// Initializes a new instance of the struct. - /// The value to associate with this . + /// Initializes a new instance of the struct. + /// The value to associate with this . [JsonConstructor] - public SessionVisibilityStatus(string value) + public LlmInferenceHttpRequestStartTransport(string value) { ArgumentException.ThrowIfNullOrWhiteSpace(value); _value = value; } - /// Gets the value associated with this . + /// Gets the value associated with this . public string Value => _value ?? string.Empty; - /// The session is visible to repository readers. - public static SessionVisibilityStatus Repo { get; } = new("repo"); + /// Plain HTTP or SSE response. Each body chunk is an opaque byte range; the response is a status line, headers, and a (possibly streamed) body. + public static LlmInferenceHttpRequestStartTransport Http { get; } = new("http"); - /// The session is restricted to its creator and collaborators. - public static SessionVisibilityStatus Unshared { get; } = new("unshared"); + /// Full-duplex WebSocket channel. Each body chunk maps to exactly one WebSocket message and the `binary` flag distinguishes text from binary frames; request and response chunks flow concurrently. + public static LlmInferenceHttpRequestStartTransport Websocket { get; } = new("websocket"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SessionVisibilityStatus left, SessionVisibilityStatus right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(LlmInferenceHttpRequestStartTransport left, LlmInferenceHttpRequestStartTransport right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SessionVisibilityStatus left, SessionVisibilityStatus right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(LlmInferenceHttpRequestStartTransport left, LlmInferenceHttpRequestStartTransport right) => !(left == right); /// - public override bool Equals(object? obj) => obj is SessionVisibilityStatus other && Equals(other); + public override bool Equals(object? obj) => obj is LlmInferenceHttpRequestStartTransport other && Equals(other); /// - public bool Equals(SessionVisibilityStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(LlmInferenceHttpRequestStartTransport other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -42695,62 +46439,62 @@ public SessionVisibilityStatus(string value) /// public override string ToString() => Value; - /// Provides a for serializing instances. + /// Provides a for serializing instances. [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter + public sealed class Converter : JsonConverter { /// - public override SessionVisibilityStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override LlmInferenceHttpRequestStartTransport Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, SessionVisibilityStatus value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, LlmInferenceHttpRequestStartTransport value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionVisibilityStatus)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(LlmInferenceHttpRequestStartTransport)); } } } -/// Why the runtime requests client-task cancellation. +/// Why the runtime is requesting a GitHub credential. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ClientTaskCancelReason : IEquatable +public readonly struct GitHubTokenAcquireReason : IEquatable { private readonly string? _value; - /// Initializes a new instance of the struct. - /// The value to associate with this . + /// Initializes a new instance of the struct. + /// The value to associate with this . [JsonConstructor] - public ClientTaskCancelReason(string value) + public GitHubTokenAcquireReason(string value) { ArgumentException.ThrowIfNullOrWhiteSpace(value); _value = value; } - /// Gets the value associated with this . + /// Gets the value associated with this . public string Value => _value ?? string.Empty; - /// A caller requested task cancellation. - public static ClientTaskCancelReason CancelRequested { get; } = new("cancel_requested"); + /// The runtime is acquiring the registration's first credential. + public static GitHubTokenAcquireReason Initial { get; } = new("initial"); - /// The session is shutting down. - public static ClientTaskCancelReason SessionShutdown { get; } = new("session_shutdown"); + /// The runtime is replacing a credential that is approaching expiry. + public static GitHubTokenAcquireReason Refresh { get; } = new("refresh"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ClientTaskCancelReason left, ClientTaskCancelReason right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(GitHubTokenAcquireReason left, GitHubTokenAcquireReason right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ClientTaskCancelReason left, ClientTaskCancelReason right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(GitHubTokenAcquireReason left, GitHubTokenAcquireReason right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ClientTaskCancelReason other && Equals(other); + public override bool Equals(object? obj) => obj is GitHubTokenAcquireReason other && Equals(other); /// - public bool Equals(ClientTaskCancelReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(GitHubTokenAcquireReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -42758,62 +46502,65 @@ public ClientTaskCancelReason(string value) /// public override string ToString() => Value; - /// Provides a for serializing instances. + /// Provides a for serializing instances. [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter + public sealed class Converter : JsonConverter { /// - public override ClientTaskCancelReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override GitHubTokenAcquireReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ClientTaskCancelReason value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, GitHubTokenAcquireReason value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ClientTaskCancelReason)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(GitHubTokenAcquireReason)); } } } -/// Error classification. +/// Explicit user decisions, never inferred from a permission grant or model response. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct SessionFsErrorCode : IEquatable +public readonly struct InstallationDecision : IEquatable { private readonly string? _value; - /// Initializes a new instance of the struct. - /// The value to associate with this . + /// Initializes a new instance of the struct. + /// The value to associate with this . [JsonConstructor] - public SessionFsErrorCode(string value) + public InstallationDecision(string value) { ArgumentException.ThrowIfNullOrWhiteSpace(value); _value = value; } - /// Gets the value associated with this . + /// Gets the value associated with this . public string Value => _value ?? string.Empty; - /// The requested path does not exist. - public static SessionFsErrorCode ENOENT { get; } = new("ENOENT"); + /// The user explicitly approved the exact review on this request. + public static InstallationDecision Confirm { get; } = new("confirm"); - /// The filesystem operation failed for an unspecified reason. - public static SessionFsErrorCode UNKNOWN { get; } = new("UNKNOWN"); + /// The user declined the reviewed operation. + public static InstallationDecision Decline { get; } = new("decline"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SessionFsErrorCode left, SessionFsErrorCode right) => left.Equals(right); + /// The user cancelled the pending decision without granting consent. + public static InstallationDecision Cancel { get; } = new("cancel"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SessionFsErrorCode left, SessionFsErrorCode right) => !(left == right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(InstallationDecision left, InstallationDecision right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(InstallationDecision left, InstallationDecision right) => !(left == right); /// - public override bool Equals(object? obj) => obj is SessionFsErrorCode other && Equals(other); + public override bool Equals(object? obj) => obj is InstallationDecision other && Equals(other); /// - public bool Equals(SessionFsErrorCode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(InstallationDecision other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -42821,693 +46568,1438 @@ public SessionFsErrorCode(string value) /// public override string ToString() => Value; - /// Provides a for serializing instances. + /// Provides a for serializing instances. [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter + public sealed class Converter : JsonConverter { /// - public override SessionFsErrorCode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override InstallationDecision Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, SessionFsErrorCode value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, InstallationDecision value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionFsErrorCode)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(InstallationDecision)); } } } -/// Entry type. +/// Provides server-scoped RPC methods (no session required). +public sealed class ServerRpc +{ + private readonly JsonRpc _rpc; + + internal ServerRpc(JsonRpc rpc) + { + _rpc = rpc; + } + + /// Checks server responsiveness and returns protocol information. + /// Optional message to echo back. + /// The to monitor for cancellation requests. The default is . + /// Server liveness response, including the echoed message, current server timestamp, and protocol version. + [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] + public async Task PingAsync(string? message = null, CancellationToken cancellationToken = default) + { + var request = new PingRequest { Message = message }; + return await CopilotClient.InvokeRpcAsync(_rpc, "ping", [request], cancellationToken); + } + + /// Performs the SDK server connection handshake and validates the optional connection token. Marked internal because this is JSON-RPC transport plumbing invoked automatically by an SDK client's own `connect()` wrapper, not a user-facing method. Stays internal as long as the SDK client owns the handshake; would only become public if the SDK ever exposed the raw schema surface to consumers without a connection wrapper. + /// Opt this connection in to GitHub telemetry forwarding for its lifetime. When set, the runtime forwards this host's telemetry across all its sessions, its sessionless events, and explicitly process-wide events over the `gitHubTelemetry.event` notification. Connections intentionally sharing one server receive that server's events; independently embedded runtime hosts do not receive each other's host-owned events. Regular events are also written to the runtime's normal GitHub/CTS path (dual-write); host-only compatibility events are forward-only and intentionally skip that path. Intended for first-party hosts that re-emit the events into their own telemetry stores. Both unrestricted and restricted events are forwarded, each tagged with a `restricted` discriminator; a backstop drops restricted events when restricted telemetry is disabled — using the process-global gate for ordinary events and an explicit session-scoped decision for host-only events. + /// Identity of the integrating host. Optional; omit it to keep the default attribution. + /// Task kinds this connection can decode when observing session tasks. Omit to retain agent and shell compatibility. + /// Connection token; required when the server was started with COPILOT_CONNECTION_TOKEN. + /// The to monitor for cancellation requests. The default is . + /// Handshake result reporting the server's protocol version and package version on success. + [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] + internal async Task ConnectAsync(bool? enableGitHubTelemetryForwarding = null, ConnectClientInfo? clientInfo = null, IList? supportedTaskKinds = null, string? token = null, CancellationToken cancellationToken = default) + { + var request = new ConnectRequest { EnableGitHubTelemetryForwarding = enableGitHubTelemetryForwarding, ClientInfo = clientInfo, SupportedTaskKinds = supportedTaskKinds, Token = token }; + return await CopilotClient.InvokeRpcAsync(_rpc, "connect", [request], cancellationToken); + } + + /// Registers the calling SDK client as the per-entrypoint extension launch provider. Call before creating any sessions. When omitted, the runtime uses its built-in extension launcher. + /// The to monitor for cancellation requests. The default is . + [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] + public async Task RegisterExtensionLaunchProviderAsync(CancellationToken cancellationToken = default) + { + await CopilotClient.InvokeRpcAsync(_rpc, "registerExtensionLaunchProvider", [], cancellationToken); + } + + /// Sandbox APIs. + public ServerSandboxApi Sandbox => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Persistence APIs. + public ServerPersistenceApi Persistence => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// User APIs. + public ServerUserApi User => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Repo APIs. + public ServerRepoApi Repo => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// FeatureFlags APIs. + public ServerFeatureFlagsApi FeatureFlags => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Permissions APIs. + public ServerPermissionsApi Permissions => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// ManagedSettings APIs. + public ServerManagedSettingsApi ManagedSettings => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Environments APIs. + public ServerEnvironmentsApi Environments => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Host APIs. + public ServerHostApi Host => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Hooks APIs. + public ServerHooksApi Hooks => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Models APIs. + public ServerModelsApi Models => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Tools APIs. + public ServerToolsApi Tools => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Account APIs. + public ServerAccountApi Account => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Secrets APIs. + public ServerSecretsApi Secrets => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Mcp APIs. + public ServerMcpApi Mcp => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Extensions APIs. + public ServerExtensionsApi Extensions => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Instructions APIs. + public ServerInstructionsApi Instructions => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Skills APIs. + public ServerSkillsApi Skills => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Catalog APIs. + public ServerCatalogApi Catalog => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Plugins APIs. + public ServerPluginsApi Plugins => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Agents APIs. + public ServerAgentsApi Agents => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// GlobalState APIs. + public ServerGlobalStateApi GlobalState => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Commands APIs. + public ServerCommandsApi Commands => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// GitHubRepository APIs. + public ServerGitHubRepositoryApi GitHubRepository => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// GitHubOwners APIs. + public ServerGitHubOwnersApi GitHubOwners => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Git APIs. + public ServerGitApi Git => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Runtime APIs. + public ServerRuntimeApi Runtime => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// SessionFs APIs. + public ServerSessionFsApi SessionFs => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// LlmInference APIs. + public ServerLlmInferenceApi LlmInference => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Sessions APIs. + public ServerSessionsApi Sessions => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// AgentRegistry APIs. + public ServerAgentRegistryApi AgentRegistry => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Connectors APIs. + public ServerConnectorsApi Connectors => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; +} + +/// Provides server-scoped Sandbox APIs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct SessionFsReaddirWithTypesEntryType : IEquatable +public sealed class ServerSandboxApi { - private readonly string? _value; + private readonly JsonRpc _rpc; - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public SessionFsReaddirWithTypesEntryType(string value) + internal ServerSandboxApi(JsonRpc rpc) { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; + _rpc = rpc; } - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; + /// Raises a user's sandbox settings to an administrator's managed sandbox floor and returns the result a session should actually run under. The floor can only tighten: an enterprise force-on is reflected even when the user's own `enabled` is false, and a user cannot lower a setting the floor raised. The result is normalized through the declared sandbox shape, so a key reorder or an undeclared passthrough key is not mistaken for a policy change by callers that diff two results. A host must call this rather than merge the two layers itself, because reimplementing the direction of the fold is what turns an administrator's floor into a suggestion. + /// The user's own `sandbox` settings slice. Omitted when the user has configured none, which still yields the managed floor rather than a disabled sandbox. + /// The effective managed `sandbox` slice acting as a floor. Omitted when no administrator policy applies, in which case the user's own settings stand unchanged. + /// The to monitor for cancellation requests. The default is . + /// The user's sandbox settings raised to the managed floor. + internal async Task ResolveFlooredConfigAsync(object? userSandbox = null, object? managedSandbox = null, CancellationToken cancellationToken = default) + { + var request = new SandboxResolveFlooredConfigParams { UserSandbox = CopilotClient.ToJsonElementForWire(userSandbox), ManagedSandbox = CopilotClient.ToJsonElementForWire(managedSandbox) }; + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.resolveFlooredConfig", [request], cancellationToken); + } - /// The entry is a file. - public static SessionFsReaddirWithTypesEntryType File { get; } = new("file"); + /// Builds the effective filesystem policy one `@`-file picker checks its suggestions against, so Git discovery and grant canonicalization run once for the picker's lifetime instead of on every debounced keystroke. Returns no policy when the sandbox is absent or disabled, which the paired filter reads as "keep every candidate". A host must pair this with `sandbox.filterPickerCandidates` rather than resolve a grant list itself, because the picker's grants are narrower than a file tool's — the same path a built-in tool may read can be one the picker must not offer. + /// The session's working directory. Git discovery and grant canonicalization are resolved against it. + /// The session's resolved `sandbox` slice. Omitted, or disabled, yields no policy and every candidate is kept. + /// The to monitor for cancellation requests. The default is . + /// The prepared file-picker policy, or none when the sandbox is disabled. + internal async Task PreparePickerPolicyAsync(string cwd, object? sandbox = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(cwd); - /// The entry is a directory. - public static SessionFsReaddirWithTypesEntryType Directory { get; } = new("directory"); + var request = new SandboxPreparePickerPolicyParams { Cwd = cwd, Sandbox = CopilotClient.ToJsonElementForWire(sandbox) }; + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.preparePickerPolicy", [request], cancellationToken); + } - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SessionFsReaddirWithTypesEntryType left, SessionFsReaddirWithTypesEntryType right) => left.Equals(right); + /// Checks a batch of `@`-file picker suggestions against a policy prepared by `sandbox.preparePickerPolicy` and returns one allow decision per candidate, positionally aligned with the request. Batched deliberately: each check canonicalizes a path component-by-component through the real filesystem, so one call amortizes that over a single hop rather than dozens while the user types. A directory the policy does not itself allow can still be kept as a waypoint to an allowed subtree. An absent policy keeps every candidate. + /// The candidates to check, in the order the answers are returned. + /// The session's working directory, matching the one the policy was prepared from. + /// The policy returned by `sandbox.preparePickerPolicy`. Omitted keeps every candidate. + /// The to monitor for cancellation requests. The default is . + /// Per-candidate allow decisions, positionally aligned with the request. + internal async Task FilterPickerCandidatesAsync(IList candidates, string cwd, object? sandbox = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(candidates); + ArgumentNullException.ThrowIfNull(cwd); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SessionFsReaddirWithTypesEntryType left, SessionFsReaddirWithTypesEntryType right) => !(left == right); + var request = new SandboxFilterPickerCandidatesParams { Candidates = candidates, Cwd = cwd, Sandbox = CopilotClient.ToJsonElementForWire(sandbox) }; + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.filterPickerCandidates", [request], cancellationToken); + } - /// - public override bool Equals(object? obj) => obj is SessionFsReaddirWithTypesEntryType other && Equals(other); + /// The sandbox a single named feature actually launches under, which is not the same as the session's sandbox. A feature is routed out of the sandbox by its own flag — `sandboxMcpServers`, `sandboxLspServers` — so an enabled session can still start one server confined and another free. Returns a disabled sandbox rather than an error when the host has no sandbox slice at all, so an absent config and an opted-out feature read alike at the launch site. A host must ask per feature rather than reuse the session config, because reading only `enabled` confines a server the user opted out of, or reports as sandboxed one that is not. + /// The routing flag naming the feature being launched, such as `sandboxMcpServers` or `sandboxLspServers`. + /// The session's resolved `sandbox` slice. Omitted when the host has none, which yields a disabled sandbox rather than an error. + /// The to monitor for cancellation requests. The default is . + /// The sandbox a named feature actually launches under. + internal async Task ResolveForOptionalAsync(string routingFlag, object? sandbox = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(routingFlag); - /// - public bool Equals(SessionFsReaddirWithTypesEntryType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + var request = new SandboxEffectiveForOptionalParams { RoutingFlag = routingFlag, Sandbox = CopilotClient.ToJsonElementForWire(sandbox) }; + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.resolveForOptional", [request], cancellationToken); + } - /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + /// Reports whether this machine can run an OS-level command sandbox at all. A host must ask before it offers sandboxing, because the answer is a property of the machine rather than of any setting: on Linux it depends on bubblewrap being installed, and on Windows on the OS build supporting BaseContainer. Probing it requires spawning a process the first time, so the answer is cached for the life of the runtime. A host that assumes support and enables the sandbox anyway produces a session in which every shell command fails. + /// The to monitor for cancellation requests. The default is . + /// True when an OS-level sandbox can run on this host. + internal async Task CheckPlatformSupportAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.checkPlatformSupport", [], cancellationToken); + } - /// - public override string ToString() => Value; + /// Reports whether this Windows host's BaseContainer implementation can reach the host's loopback interface directly, which is what a local-network policy needs to avoid routing through the egress proxy. Like `sandbox.checkPlatformSupport`, the answer is a property of the machine's OS build rather than of any setting, and resolving it the first time spawns a platform probe. A host that offers the direct local-network option on a machine that lacks the capability produces a session whose sandboxed commands cannot reach local services. Always false off Windows. + /// The to monitor for cancellation requests. The default is . + /// True when the sandbox on this host can reach host loopback directly. + internal async Task CheckWindowsHostLoopbackSupportAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.checkWindowsHostLoopbackSupport", [], cancellationToken); + } - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter + /// The explanation a host shows when it refuses an explicit request to turn the sandbox on for a machine that cannot run one. Carries the platform-specific remedy — install bubblewrap, update Windows — and the documentation link, so a host that got `false` from `sandbox.checkPlatformSupport` does not have to compose its own reason and cannot describe the same machine differently from the runtime's own spawn path. + /// The to monitor for cancellation requests. The default is . + /// The user-facing refusal, including the platform remedy and the documentation link. + internal async Task GetUnsupportedMessageAsync(CancellationToken cancellationToken = default) { - /// - public override SessionFsReaddirWithTypesEntryType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.getUnsupportedMessage", [], cancellationToken); + } - /// - public override void Write(Utf8JsonWriter writer, SessionFsReaddirWithTypesEntryType value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionFsReaddirWithTypesEntryType)); - } + /// The notice a host shows when a stored `sandbox` setting is being ignored for this session because the machine cannot enforce it. Distinct from `sandbox.getUnsupportedMessage`, which refuses an explicit request: this one reports that a setting the user already saved is inert, states that commands will run unsandboxed, and confirms the stored setting itself is left alone. A host that stays silent here leaves the user believing a sandbox is active. + /// The to monitor for cancellation requests. The default is . + /// The user-facing notice that a saved sandbox setting is inert for this session. + internal async Task GetUnsupportedSettingDisabledNoticeAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.getUnsupportedSettingDisabledNotice", [], cancellationToken); } -} + /// The warning a host prints at startup when a session asked for a sandbox on a machine that cannot run one. The third of the unsupported notices, and the only one that depends on the policy in force: it names the remedy the reader can actually act on, which is the stored setting for an unmanaged host, an administrator for a managed one, and waiting for the policy to resolve when the sandbox stayed on only because the policy could not be determined. Returns no notice when the machine does support a sandbox, so a host can call it unconditionally on the startup path. A host that composes its own warning tells a managed user to change a setting their policy will overwrite. + /// The managed `sandbox` slice, when a policy supplies one. The notice treats the sandbox as enforced only when this both enables it and sets `failIfUnavailable`, because a managed sandbox that tolerates being unavailable still leaves the user able to turn it off. + /// True when the sandbox stayed on only because the managed policy could not be determined. Distinguishes a policy that positively enforces the sandbox from one that merely failed to resolve, which is the difference between telling the user to contact an administrator and telling them to wait. Defaults to false. + /// The to monitor for cancellation requests. The default is . + /// The startup warning, absent when the machine can run a sandbox. + internal async Task GetUnsupportedStartupNoticeAsync(object? managedSandbox = null, bool? enabledByUndeterminedPolicy = null, CancellationToken cancellationToken = default) + { + var request = new SandboxUnsupportedStartupNoticeParams { ManagedSandbox = CopilotClient.ToJsonElementForWire(managedSandbox), EnabledByUndeterminedPolicy = enabledByUndeterminedPolicy }; + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.getUnsupportedStartupNotice", [request], cancellationToken); + } -/// How to execute the query: 'exec' for DDL/multi-statement (no results), 'query' for SELECT (returns rows), 'run' for INSERT/UPDATE/DELETE (returns rowsAffected). -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct SessionFsSqliteQueryType : IEquatable -{ - private readonly string? _value; + /// Whether one slash command stays reachable while sandbox enforcement is unavailable and the session is otherwise blocked. The permitted set is deliberately narrow — commands that only report state, and the few that let the user repair or leave the situation — and it is keyed on the arguments as well as the name, so `/sandbox status` is admitted where `/sandbox enable` is not. A host must ask rather than keep its own list: a command this returns false for is one the runtime would refuse anyway, and a divergent list either strands the user with no way out or admits a command that acts on an unenforced session. + /// The command name as the user typed it, with or without its leading slash. + /// The command's arguments, in order. Pass an empty array for a bare command: several entries in the allowlist admit the bare form and refuse every argument. + /// The to monitor for cancellation requests. The default is . + /// True when the command stays available while enforcement is blocked. + internal async Task CheckEnforcementCommandAsync(string commandName, IList args, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(commandName); + ArgumentNullException.ThrowIfNull(args); - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public SessionFsSqliteQueryType(string value) + var request = new SandboxEnforcementCommandAllowedParams { CommandName = commandName, Args = args }; + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.checkEnforcementCommand", [request], cancellationToken); + } + + /// Returns the documentation URL a host should point a user at when it declines or downgrades a sandbox setting. The runtime owns the address so every surface cites the same page and a host does not hard-code a link that later moves. + /// The to monitor for cancellation requests. The default is . + /// The documentation URL a host cites when it declines or downgrades a sandbox setting. + internal async Task GetDocsUrlAsync(CancellationToken cancellationToken = default) { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.getDocsUrl", [], cancellationToken); } - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; + /// Reports whether the host running this runtime can run the command sandbox, without starting a session or spawning a sandboxed command. + /// The to monitor for cancellation requests. The default is . + /// Whether the host running this runtime can run the command sandbox. The runtime checks `supported` once per process. A capability answer can change while the process runs, for example after the user installs a missing package. + public async Task GetHostSupportAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.getHostSupport", [], cancellationToken); + } - /// Execute DDL or multi-statement SQL without returning rows. - public static SessionFsSqliteQueryType Exec { get; } = new("exec"); + /// ProxyCa APIs. + public ServerSandboxProxyCaApi ProxyCa => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; - /// Execute a SELECT-style query and return rows. - public static SessionFsSqliteQueryType Query { get; } = new("query"); + /// ProxySecret APIs. + public ServerSandboxProxySecretApi ProxySecret => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; - /// Execute INSERT, UPDATE, or DELETE SQL and return affected-row metadata. - public static SessionFsSqliteQueryType Run { get; } = new("run"); + /// Settings APIs. + public ServerSandboxSettingsApi Settings => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; +} - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SessionFsSqliteQueryType left, SessionFsSqliteQueryType right) => left.Equals(right); +/// Provides server-scoped SandboxProxyCa APIs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ServerSandboxProxyCaApi +{ + private readonly JsonRpc _rpc; - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SessionFsSqliteQueryType left, SessionFsSqliteQueryType right) => !(left == right); + internal ServerSandboxProxyCaApi(JsonRpc rpc) + { + _rpc = rpc; + } - /// - public override bool Equals(object? obj) => obj is SessionFsSqliteQueryType other && Equals(other); + /// Reports whether this runtime process can offer installing the sandbox credential-proxy certificate authority into OS trust. + /// The to monitor for cancellation requests. The default is . + /// True when this process can offer installing the sandbox credential-proxy certificate authority into OS trust. + public async Task CheckInstallationSupportAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.proxyCa.checkInstallationSupport", [], cancellationToken); + } - /// - public bool Equals(SessionFsSqliteQueryType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + /// Reports whether this sandbox configuration uses the credential proxy and can need certificate-authority setup. + /// The sandbox configuration that the host gives its sessions. The runtime reads the credential hosts from `auth` and `credentials`; it ignores `enabled` and the other fields. + /// The to monitor for cancellation requests. The default is . + /// True when the sandbox configuration uses the credential proxy and can need certificate-authority setup. + public async Task CheckRelevanceAsync(SandboxConfig? sandboxConfig = null, CancellationToken cancellationToken = default) + { + var request = new SandboxProxyCaRequest { SandboxConfig = sandboxConfig }; + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.proxyCa.checkRelevance", [request], cancellationToken); + } - /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + /// Reports whether the persistent certificate authority of the sandbox credential proxy exists, whether OS trust includes it, and whether it must be rotated. Changes nothing. + /// The sandbox configuration that the host gives its sessions. The runtime reads the credential hosts from `auth` and `credentials`; it ignores `enabled` and the other fields. + /// The to monitor for cancellation requests. The default is . + /// Status of the persistent certificate authority of the sandbox credential proxy. + public async Task GetStatusAsync(SandboxConfig? sandboxConfig = null, CancellationToken cancellationToken = default) + { + var request = new SandboxProxyCaRequest { SandboxConfig = sandboxConfig }; + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.proxyCa.getStatus", [request], cancellationToken); + } - /// - public override string ToString() => Value; + /// Creates the persistent certificate authority of the sandbox credential proxy if none is stored, without changing OS trust, and returns the path of its public certificate. Keeps an existing certificate authority, even one that must be rotated. Fails where OS trust is unsupported. Trust it with sandbox.proxyCa.trust: the CLI trusts only the hosts in the saved user settings, so it refuses a certificate authority that also covers hosts from sandboxConfig. + /// The sandbox configuration that the host gives its sessions. The runtime reads the credential hosts from `auth` and `credentials`; it ignores `enabled` and the other fields. + /// The to monitor for cancellation requests. The default is . + /// Result of creating the persistent certificate authority of the sandbox credential proxy. + public async Task CreateAsync(SandboxConfig? sandboxConfig = null, CancellationToken cancellationToken = default) + { + var request = new SandboxProxyCaRequest { SandboxConfig = sandboxConfig }; + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.proxyCa.create", [request], cancellationToken); + } - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter + /// Replaces the persistent certificate authority of the sandbox credential proxy with a new one for the current credential hosts. If OS trust included the old one, removes it and trusts the new one, which can show an OS authentication prompt. Running sandboxed tools keep the old certificate authority until they restart. + /// The sandbox configuration that the host gives its sessions. The runtime reads the credential hosts from `auth` and `credentials`; it ignores `enabled` and the other fields. + /// The to monitor for cancellation requests. The default is . + /// Status of the persistent certificate authority of the sandbox credential proxy. + public async Task RotateAsync(SandboxConfig? sandboxConfig = null, CancellationToken cancellationToken = default) { - /// - public override SessionFsSqliteQueryType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } + var request = new SandboxProxyCaRequest { SandboxConfig = sandboxConfig }; + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.proxyCa.rotate", [request], cancellationToken); + } - /// - public override void Write(Utf8JsonWriter writer, SessionFsSqliteQueryType value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionFsSqliteQueryType)); - } + /// Adds the persistent certificate authority of the sandbox credential proxy to OS trust, so sandboxed clients that read only OS trust accept the proxy. Call create first. Refuses a certificate authority that is not constrained to the current credential hosts. Can show an OS authentication prompt. + /// The sandbox configuration that the host gives its sessions. The runtime reads the credential hosts from `auth` and `credentials`; it ignores `enabled` and the other fields. + /// The to monitor for cancellation requests. The default is . + /// Status of the persistent certificate authority of the sandbox credential proxy. + public async Task TrustAsync(SandboxConfig? sandboxConfig = null, CancellationToken cancellationToken = default) + { + var request = new SandboxProxyCaRequest { SandboxConfig = sandboxConfig }; + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.proxyCa.trust", [request], cancellationToken); } -} + /// Removes the persistent certificate authority of the sandbox credential proxy from OS trust. Keeps the stored certificate authority. Can show an OS authentication prompt. Sandboxed clients that read only OS trust then reject the proxy; clients that read the per-process certificate bundle continue to work. + /// The to monitor for cancellation requests. The default is . + /// Status of the persistent certificate authority of the sandbox credential proxy. + public async Task RemoveAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.proxyCa.remove", [], cancellationToken); + } +} -/// SQLite transaction failure classification. +/// Provides server-scoped SandboxProxySecret APIs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct SessionFsSqliteTransactionErrorClass : IEquatable +public sealed class ServerSandboxProxySecretApi { - private readonly string? _value; + private readonly JsonRpc _rpc; - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public SessionFsSqliteTransactionErrorClass(string value) + internal ServerSandboxProxySecretApi(JsonRpc rpc) { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; + _rpc = rpc; } - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; + /// Reports where this runtime keeps the sandbox proxy password: the credential store directory and the entry identifier within it. Both are derived from the configuration directory this runtime was started with, so a host does not resolve them itself. Read this to recognize a `${secret:…}` value in stored sandbox settings as this runtime's own proxy password rather than a reference the user wrote by hand. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract. + /// The to monitor for cancellation requests. The default is . + /// Where this runtime keeps the sandbox proxy password. + internal async Task GetLocationAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.proxySecret.getLocation", [], cancellationToken); + } - /// SQLite reported BUSY or LOCKED before commit; the transaction was rolled back and may be retried. - public static SessionFsSqliteTransactionErrorClass BusyOrLocked { get; } = new("busyOrLocked"); + /// Reads the stored sandbox proxy password from the credential store `sandbox.proxySecret.getLocation` names, preferring the OS keychain and falling back to the on-disk store. Reports an absent value rather than failing when no entry exists. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract. + /// The to monitor for cancellation requests. The default is . + /// The stored sandbox proxy password, if one is present. + internal async Task GetAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.proxySecret.get", [], cancellationToken); + } - /// The statement, database, or provider failed definitively and must not be retried automatically. - public static SessionFsSqliteTransactionErrorClass Fatal { get; } = new("fatal"); + /// Stores the sandbox proxy password in the credential store `sandbox.proxySecret.getLocation` names, replacing any existing entry. Reports whether the OS keychain accepted it or it fell back to the on-disk store. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract. + /// The password to store for the sandbox proxy. + /// The to monitor for cancellation requests. The default is . + /// Where the sandbox proxy password was stored. + internal async Task SaveAsync(string value, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(value); - /// The transport failed after the provider may have committed; retrying could duplicate effects. - public static SessionFsSqliteTransactionErrorClass PostCommitAmbiguous { get; } = new("postCommitAmbiguous"); + var request = new SandboxProxySecretSaveRequest { Value = value }; + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.proxySecret.save", [request], cancellationToken); + } - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SessionFsSqliteTransactionErrorClass left, SessionFsSqliteTransactionErrorClass right) => left.Equals(right); + /// Removes the stored sandbox proxy password from the credential store `sandbox.proxySecret.getLocation` names. Succeeds when no entry exists, so it is safe to call while clearing a proxy password whose origin is unknown. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract. + /// The to monitor for cancellation requests. The default is . + internal async Task DeleteAsync(CancellationToken cancellationToken = default) + { + await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.proxySecret.delete", [], cancellationToken); + } +} - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SessionFsSqliteTransactionErrorClass left, SessionFsSqliteTransactionErrorClass right) => !(left == right); +/// Provides server-scoped SandboxSettings APIs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ServerSandboxSettingsApi +{ + private readonly JsonRpc _rpc; - /// - public override bool Equals(object? obj) => obj is SessionFsSqliteTransactionErrorClass other && Equals(other); + internal ServerSandboxSettingsApi(JsonRpc rpc) + { + _rpc = rpc; + } - /// - public bool Equals(SessionFsSqliteTransactionErrorClass other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + /// Resolves a stored sandbox settings object into the effective one, filling every value the user left unset with its default. Read this to render or reason about the sandbox configuration a session would actually run under, rather than the sparse document the user wrote. + /// The sandbox settings as stored in the settings file. An empty object resolves to the defaults alone. + /// The to monitor for cancellation requests. The default is . + /// The effective sandbox settings, with every unset value resolved to its default. + internal async Task> ApplyDefaultsAsync(object? stored, CancellationToken cancellationToken = default) + { + var request = new SandboxApplyDefaultsRequest { Stored = CopilotClient.ToJsonElementForWire(stored) ?? JsonElement.Parse("null") }; + return await CopilotClient.InvokeRpcAsync>(_rpc, "sandbox.settings.applyDefaults", [request], cancellationToken); + } +} - /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); +/// Provides server-scoped Persistence APIs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ServerPersistenceApi +{ + private readonly JsonRpc _rpc; - /// - public override string ToString() => Value; + internal ServerPersistenceApi(JsonRpc rpc) + { + _rpc = rpc; + } - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter + /// Atomically replaces a JSON file's entire contents while holding the runtime's per-path lock. The lock lives in the runtime process, so a host that writes these files itself takes no part in it and can tear a concurrent runtime write; route the write here instead. Unlike a merging write this never reads or parses the existing file, so a previously corrupted snapshot is replaced wholesale rather than failing every later write. Use it only for callers that always persist a complete snapshot. + /// Absolute path of the file to replace. Parent directories are created when missing. + /// The serialized JSON document to write. It replaces the file's contents entirely; nothing is merged. + /// Comment lines to emit above the document, for a format that tolerates them. Omit for strict JSON. + /// The to monitor for cancellation requests. The default is . + /// The outcome of a locked snapshot write. + internal async Task WriteRawJsonLockedAsync(string path, string dataJson, string? fileHeader = null, CancellationToken cancellationToken = default) { - /// - public override SessionFsSqliteTransactionErrorClass Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } + ArgumentNullException.ThrowIfNull(path); + ArgumentNullException.ThrowIfNull(dataJson); - /// - public override void Write(Utf8JsonWriter writer, SessionFsSqliteTransactionErrorClass value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionFsSqliteTransactionErrorClass)); - } + var request = new PersistenceWriteRawJsonLockedRequest { Path = path, DataJson = dataJson, FileHeader = fileHeader }; + return await CopilotClient.InvokeRpcAsync(_rpc, "persistence.writeRawJsonLocked", [request], cancellationToken); } } - -/// Defines the allowed values. +/// Provides server-scoped User APIs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct HostExitReason : IEquatable +public sealed class ServerUserApi { - private readonly string? _value; + private readonly JsonRpc _rpc; - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public HostExitReason(string value) + internal ServerUserApi(JsonRpc rpc) { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; + _rpc = rpc; } - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// The owner requested disposal. - public static HostExitReason Disposed { get; } = new("disposed"); + /// Settings APIs. + public ServerUserSettingsApi Settings => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; +} - /// The hosting task or its SDK transport exited; this does not mean the runtime process exited. - public static HostExitReason Exited { get; } = new("exited"); +/// Provides server-scoped UserSettings APIs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ServerUserSettingsApi +{ + private readonly JsonRpc _rpc; - /// The owning SDK connection disconnected. - public static HostExitReason OwnerDisconnected { get; } = new("ownerDisconnected"); + internal ServerUserSettingsApi(JsonRpc rpc) + { + _rpc = rpc; + } - /// The runtime is shutting down. - public static HostExitReason RuntimeShutdown { get; } = new("runtimeShutdown"); + /// Adds or removes one hook key in the user `disabledHooks` setting, rewriting settings.json in place and leaving every other setting untouched. Reports a typed outcome rather than failing the call, so a host can tell an unparseable settings file apart from a write that could not complete. + /// Path of the settings.json to rewrite. + /// Durable content-hash key of the hook, as `hooks.loadResources` reports it. + /// True adds the key to `disabledHooks`, false removes it. + /// The to monitor for cancellation requests. The default is . + /// Outcome of rewriting the `disabledHooks` setting. + internal async Task SetHookDisabledAsync(string settingsPath, string disableKey, bool disabled, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(settingsPath); + ArgumentNullException.ThrowIfNull(disableKey); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(HostExitReason left, HostExitReason right) => left.Equals(right); + var request = new UserSettingsSetHookDisabledRequest { SettingsPath = settingsPath, DisableKey = disableKey, Disabled = disabled }; + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.setHookDisabled", [request], cancellationToken); + } - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(HostExitReason left, HostExitReason right) => !(left == right); + /// Reads the user's settings document exactly as `user.settings.load` does, but from a caller-supplied configuration directory instead of the one the server resolved for itself. Use this when a consumer scopes a session to its own Copilot home — the SDK's per-session `configDir` override — so the settings read match the directory that session actually uses. An absent or empty `configDir` resolves the server's own home, making this identical to `user.settings.load`. Hook-validation relaxations are deliberately not caller-supplied: they continue to come from the server's environment, so a consumer cannot widen the host's hook policy through this method. + /// Copilot configuration directory to read the settings document from, taking precedence over the server's own `COPILOT_HOME` and default home. Omit it, or pass an empty string, to read the directory the server resolved for itself. + /// The to monitor for cancellation requests. The default is . + /// The stored user settings document, keyed by setting name. + internal async Task> LoadForConfigDirAsync(string? configDir = null, CancellationToken cancellationToken = default) + { + var request = new UserSettingsLoadForConfigDirRequest { ConfigDir = configDir }; + return await CopilotClient.InvokeRpcAsync>(_rpc, "user.settings.loadForConfigDir", [request], cancellationToken); + } - /// - public override bool Equals(object? obj) => obj is HostExitReason other && Equals(other); + /// Reads a repository's checked-in settings document as stored, normalized, together with any diagnostic raised while reading it. This is the repository half of the layering `user.settings.get` reports: `user.settings.load` reads the user's own document, and this reads the one a repository checks in to govern the keys `user.settings.governanceKeys` lists as repo-governable. A document that fails to parse, or that sets a key a repository may not govern, is reported through `warning` while the settings that did load are still returned, so a consumer surfaces the diagnostic and continues rather than failing the session. When `repoRoot` is the user's home directory the Claude-compatible configuration is skipped, so a home-directory session does not inherit a personal config as repository policy. + /// Absolute path of the repository root whose settings to read. When this is the user's home directory, the Claude-compatible configuration is skipped, so a home-directory session does not inherit a personal config as if it were repository policy. + /// The to monitor for cancellation requests. The default is . + /// A repository's settings as stored, together with any diagnostic raised while reading them. + internal async Task LoadRepoAsync(string repoRoot, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(repoRoot); - /// - public bool Equals(HostExitReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + var request = new UserSettingsLoadRepoRequest { RepoRoot = repoRoot }; + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.loadRepo", [request], cancellationToken); + } - /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + /// Reads a repository's settings and merges them over a supplied user layer in one step, returning the settings a consumer should act on inside that repository. Use this rather than reading the two layers separately: the merge is per setting and collection-aware, so a repository adds to the collections it is allowed to influence without replacing the user's own entries, and a whole-document overlay would get that wrong. Any diagnostic raised while reading the repository layer is returned rather than printed, so the consumer decides how to surface it; the settings that did load are merged regardless. + /// Absolute path of the repository root whose settings to read. When this is the user's home directory, the Claude-compatible configuration is skipped, so a home-directory session does not inherit a personal config as if it were repository policy. + /// The user's own settings, keyed by setting name. The repository layer is merged over this; a repository may only add to the collection settings it is allowed to influence, so a user setting it does not name survives unchanged. + /// The to monitor for cancellation requests. The default is . + /// The settings a consumer should act on inside a repository, together with any diagnostic raised while reading the repository layer. + internal async Task LoadAndMergeRepoAsync(string repoRoot, IDictionary userSettings, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(repoRoot); + ArgumentNullException.ThrowIfNull(userSettings); - /// - public override string ToString() => Value; + var request = new UserSettingsLoadAndMergeRepoRequest { RepoRoot = repoRoot, UserSettings = userSettings }; + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.loadAndMergeRepo", [request], cancellationToken); + } - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter + /// Reports the on-disk path of the settings document `user.settings.load` and `user.settings.set` read and write. Resolved the same way those methods resolve it, so a host that shows or opens the settings file names the file the runtime actually uses, including the extensionless legacy `settings` file when that is what is present. + /// The to monitor for cancellation requests. The default is . + /// The on-disk location of the user's settings document. + internal async Task GetPathAsync(CancellationToken cancellationToken = default) { - /// - public override HostExitReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, HostExitReason value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(HostExitReason)); - } + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.getPath", [], cancellationToken); } -} + /// Reads the user's stored settings together with the diagnostic raised while reading them. This is `user.settings.load` plus the reason part of the document was ignored: that one answers the document alone, so a caller cannot tell a clean read from a salvaged one. Read this when the caller is about to write the file back, where accepting a partial parse would discard every value the loader could not read. The settings that did load are still reported alongside the warning, so a consumer may surface it and continue instead of failing. + /// The to monitor for cancellation requests. The default is . + /// The user's stored settings, together with any diagnostic raised while reading them. + internal async Task LoadWithWarningAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.loadWithWarning", [], cancellationToken); + } -/// Transport the runtime would otherwise use for this request. `http` (the default when absent) covers plain HTTP and SSE responses; `websocket` indicates a full-duplex message channel where each body chunk maps to one WebSocket message and the `binary` flag distinguishes text from binary frames. The SDK consumer uses this to decide whether to service the request with an HTTP client or a WebSocket client. It is the one piece of request metadata the consumer cannot reliably infer from the URL or headers alone. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct LlmInferenceHttpRequestStartTransport : IEquatable -{ - private readonly string? _value; + /// Folds a user settings layer over a managed (enterprise policy) layer and returns the merged settings. The managed layer's governance keys win, `autoTier` resolves through its overridable form, and the two per-entry control maps (`enabledPlugins`, `extraKnownMarketplaces`) union across the layers and are then stamped back from the managed layer, so each entry an administrator declares wins while the user keeps control of the rest. The parameters are named for their layers rather than being a generic base/override pair: nothing about a settings object identifies which layer produced it, so the runtime cannot detect a transposed call, and transposing one resolves the governance keys in the user's favour instead of the administrator's. Supplying only one layer returns it unchanged, and supplying neither returns an empty object. Rejects a managed `autoTier` that is neither a user-configurable tier nor a single-key overridable form, rather than silently dropping it. + /// The managed layer. Its governance keys win, and the per-entry `enabledPlugins` and `extraKnownMarketplaces` control maps are stamped back from it after the union. Omitted when the host is unmanaged. + /// The user's own settings layer. Wins on every key the managed layer does not govern. Omitted when the user has no settings yet. + /// The to monitor for cancellation requests. The default is . + /// The user layer folded over the managed layer. + internal async Task MergeManagedAndUserAsync(object? managed = null, object? user = null, CancellationToken cancellationToken = default) + { + var request = new UserSettingsMergeManagedAndUserParams { Managed = CopilotClient.ToJsonElementForWire(managed), User = CopilotClient.ToJsonElementForWire(user) }; + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.mergeManagedAndUser", [request], cancellationToken); + } - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public LlmInferenceHttpRequestStartTransport(string value) + /// Folds a repository's settings layer into the user layer. Each repo-overridable key carries a directional policy chosen so a repository can only add restrictions and never relax the user's own. The two per-entry managed control maps are the exception those directional rules cannot express — a repository may legitimately enable plugins and declare marketplaces — so they are re-stamped afterwards from the runtime's ambient managed policy. That makes the result depend on the runtime process's managed state and not on the arguments alone: a host must call this rather than reimplement the fold, or a repository could relax a restriction the administrator imposed. Rejects when either layer is not a JSON object. + /// The user's settings, already resolved against any managed layer. + /// The repository's checked-in settings layer. + /// The to monitor for cancellation requests. The default is . + /// The repository layer folded into the user layer. + internal async Task MergeRepoValueAsync(object? userSettings, object? repoSettings, CancellationToken cancellationToken = default) { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; + var request = new UserSettingsMergeRepoValueParams { UserSettings = CopilotClient.ToJsonElementForWire(userSettings) ?? JsonElement.Parse("null"), RepoSettings = CopilotClient.ToJsonElementForWire(repoSettings) ?? JsonElement.Parse("null") }; + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.mergeRepoValue", [request], cancellationToken); } - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; + /// Runs the one-time migration that moves a staff account's update channel to stable, and records that it ran. Idempotent and self-gating: it takes a lock over the global-state file, re-reads the completion stamp inside it, and returns `current` rather than migrating twice, so two hosts racing at startup cannot both migrate. Only a `migrated` status means the persisted settings changed and the caller should reload them. An account on the experimental channel is deliberately left alone and reported as `excluded-experimental`. + /// The user's home directory, used when neither a configuration directory nor `COPILOT_HOME` is set. + /// The host's environment variables. `COPILOT_HOME` is read from here, as are the variables a settings hook may expand. + /// The Copilot configuration directory, when the host set one. Takes precedence over `COPILOT_HOME` and the home directory. + /// The to monitor for cancellation requests. The default is . + /// The outcome of the one-time staff update-channel migration. + internal async Task MigrateStaffUpdateChannelOnceAsync(string homeDirectory, IDictionary environment, string? configDir = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(homeDirectory); + ArgumentNullException.ThrowIfNull(environment); - /// Plain HTTP or SSE response. Each body chunk is an opaque byte range; the response is a status line, headers, and a (possibly streamed) body. - public static LlmInferenceHttpRequestStartTransport Http { get; } = new("http"); + var request = new UserSettingsHostContext { HomeDirectory = homeDirectory, Environment = environment, ConfigDir = configDir }; + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.migrateStaffUpdateChannelOnce", [request], cancellationToken); + } - /// Full-duplex WebSocket channel. Each body chunk maps to exactly one WebSocket message and the `binary` flag distinguishes text from binary frames; request and response chunks flow concurrently. - public static LlmInferenceHttpRequestStartTransport Websocket { get; } = new("websocket"); + /// Runs the one-time reset that clears a staff account's pinned model selection, and reports the timestamp the host should record. Idempotent and self-gating: the reset is gated on a fixed cutoff, so a caller that passes back the timestamp it recorded gets `didReset: false` rather than a second reset. It applies to staff accounts only; any other account is reported as not reset without the settings document being opened. Only `didReset: true` means the persisted settings changed, and only then are the updated settings returned for the host to adopt. + /// The configuration directory, home directory and environment to resolve the settings and global-state documents from. A host that scopes a session to its own Copilot home must pass that directory here, or the reset is evaluated against the server's own home instead. + /// Whether the signed-in account is a staff account. The reset applies only to staff, so anything other than `true` returns `didReset: false` without touching the settings document. + /// When the caller last recorded this reset, as an ISO-8601 timestamp. A timestamp at or after the cutoff means the reset already ran and is returned unchanged. + /// The to monitor for cancellation requests. The default is . + /// The outcome of the one-time staff model-selection reset. + internal async Task ResetStaffModelSelectionOnceAsync(UserSettingsHostContext context, bool? staff = null, string? staffModelResetAt = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(context); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(LlmInferenceHttpRequestStartTransport left, LlmInferenceHttpRequestStartTransport right) => left.Equals(right); + var request = new UserSettingsResetStaffModelSelectionOnceRequest { Context = context, Staff = staff, StaffModelResetAt = staffModelResetAt }; + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.resetStaffModelSelectionOnce", [request], cancellationToken); + } - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(LlmInferenceHttpRequestStartTransport left, LlmInferenceHttpRequestStartTransport right) => !(left == right); + /// Reports the top-level keys in the user settings document that the runtime does not recognize, phrased for display to the user. This is a separate question from the warning `user.settings.loadWithWarning` returns: that one describes parts of the document the loader had to ignore to load the rest, whereas an unknown key parses cleanly and is simply not a setting, so a host that surfaces only the load warning silently accepts a misspelled key that will never take effect. Nothing is reported when the document is absent, unreadable, or failed to load, because an unknown-key list derived from a document the runtime could not read would be misleading rather than helpful. Inspection never modifies the document. + /// The configuration directory, home directory and environment to resolve the settings document from. A host that scopes a session to its own Copilot home must pass that directory here, or the server's own home is inspected instead. + /// The to monitor for cancellation requests. The default is . + /// The unrecognized top-level keys found in the user settings document, already phrased for display. + internal async Task GetUnknownKeysWarningAsync(UserSettingsHostContext context, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(context); - /// - public override bool Equals(object? obj) => obj is LlmInferenceHttpRequestStartTransport other && Equals(other); + var request = new UserSettingsUnknownKeysWarningRequest { Context = context }; + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.getUnknownKeysWarning", [request], cancellationToken); + } - /// - public bool Equals(LlmInferenceHttpRequestStartTransport other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + /// Resolves the effective runtime settings for a host that is about to start a session, layering the given overrides over the settings documents, the environment and the platform defaults. A host cannot assemble this itself: the precedence between a user document, a repository document, the managed layers and the environment is the runtime's own rule, and a host that re-implements it drifts from the server it is configuring. The working directory, environment and platform are taken as arguments rather than read from the server process, so a host resolving settings for a directory other than its own gets that directory's answer. Nothing is written; this only computes. + /// The directory the settings are being resolved for. This selects the repository-scoped document, so a host resolving settings for a directory other than its own process working directory must pass that directory here. + /// The environment to read settings-bearing variables from. Passed explicitly rather than read from the server process so a host can resolve settings for an environment it is about to create rather than the one it runs in. + /// The platform to apply platform-specific defaults for, in the same spelling the host's own runtime reports. + /// Settings that take precedence over every document and environment layer, such as the values a host derived from its own command line. Keyed by setting name, with each value carried verbatim, so a host may pass keys this runtime does not recognize. Absent when the host contributes no overrides of its own. + /// The to monitor for cancellation requests. The default is . + /// The effective runtime settings, keyed by setting name. Keys this runtime does not recognize are preserved rather than dropped, so a caller reading a document written by a newer runtime keeps every value. + internal async Task> BuildRuntimeAsync(string workingDirectory, IDictionary environment, string platform, IDictionary? overrides = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(workingDirectory); + ArgumentNullException.ThrowIfNull(environment); + ArgumentNullException.ThrowIfNull(platform); - /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + var request = new UserSettingsBuildRuntimeRequest { WorkingDirectory = workingDirectory, Environment = environment, Platform = platform, Overrides = overrides }; + return await CopilotClient.InvokeRpcAsync>(_rpc, "user.settings.buildRuntime", [request], cancellationToken); + } - /// - public override string ToString() => Value; + /// Runs the one-time migration that moves a staff account's log level to the current default, and records that it ran. Idempotent and self-gating: it takes a lock over the global-state file and re-reads the completion stamp inside it, so two hosts racing at startup cannot both migrate. Only `refreshed` can have changed the persisted settings, and the settings it returns are the document re-read after the write, so a host should adopt them rather than reloading separately and racing the next writer. + /// The configuration directory, home directory and environment to resolve the settings and global-state documents from. A host that scopes a session to its own Copilot home must pass that directory here, or the migration is evaluated against the server's own home instead. + /// When the caller last recorded this migration, as an ISO-8601 timestamp. Supplying it lets the runtime answer `current` without re-running the evaluation. + /// The to monitor for cancellation requests. The default is . + /// The outcome of the one-time staff log-level migration. + internal async Task MigrateStaffLogLevelOnceAsync(UserSettingsHostContext context, string? cachedCompletedAt = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(context); - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter + var request = new UserSettingsMigrateStaffLogLevelOnceRequest { Context = context, CachedCompletedAt = cachedCompletedAt }; + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.migrateStaffLogLevelOnce", [request], cancellationToken); + } + + /// Runs the one-time cleanup that removes obsolete CLI-owned settings and legacy rubber-duck keys from the user's settings document, and reports what it did. Idempotent and safe to call on every startup: with nothing obsolete left it reports `unchanged` and writes nothing. The cleanup preserves every key it does not recognize, so a setting written by a newer host is never dropped by an older one, and a document that fails to parse is reported `invalid` and left untouched rather than rewritten from a partial read. The cleaned document is not returned: it is written before this replies, so `user.settings.load` reads it authoritatively. A host that surfaces unrecognized keys through `user.settings.unknownKeys` wants this first, because the keys this retires would otherwise be shown to the user as if they were misspelled settings. + /// The configuration directory holding the settings document to clean. A host that scopes a session to its own Copilot home passes that directory here; omit it to clean the document under the home the server resolved for itself. The home directory and environment are deliberately not caller-supplied: they decide only the settings path root and the two hook-parsing flags, and those describe the process doing the reading rather than the caller. + /// The to monitor for cancellation requests. The default is . + /// The outcome of the one-time obsolete-settings cleanup. + internal async Task MigrateObsoleteOnDiskAsync(string? configDir = null, CancellationToken cancellationToken = default) { - /// - public override LlmInferenceHttpRequestStartTransport Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } + var request = new UserSettingsMigrateObsoleteOnDiskRequest { ConfigDir = configDir }; + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.migrateObsoleteOnDisk", [request], cancellationToken); + } - /// - public override void Write(Utf8JsonWriter writer, LlmInferenceHttpRequestStartTransport value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(LlmInferenceHttpRequestStartTransport)); - } + /// Lists every known user setting from settings.json, each with its effective value, its default, and whether it is at the default — so settings the user has never set still appear with their default value. Does not include repository- or enterprise-managed overrides that the runtime layers on top at session time. + /// The to monitor for cancellation requests. The default is . + /// Per-key metadata for every known user setting in settings.json, including settings left at their default. Excludes repository- and enterprise-managed overrides. + public async Task GetAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.get", [], cancellationToken); } -} + /// Resolves the on-disk locations of the settings files this runtime reads, so a consumer can show or open them. The user path is the same file `user.settings.get`, `user.settings.load`, and `user.settings.set` operate on. Supply `repoRoot` to also resolve that repository's checked-in and local settings paths. A path is reported whether or not the file exists. + /// Absolute path of a repository root. Supply it to also resolve that repository's two settings file paths; omit it to resolve only the user path. + /// The to monitor for cancellation requests. The default is . + /// On-disk locations of the settings files this runtime reads. A path is reported whether or not the file exists. + internal async Task GetPathsAsync(string? repoRoot = null, CancellationToken cancellationToken = default) + { + var request = new UserSettingsGetPathsRequest { RepoRoot = repoRoot }; + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.getPaths", [request], cancellationToken); + } -/// Why the runtime is requesting a GitHub credential. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct GitHubTokenAcquireReason : IEquatable -{ - private readonly string? _value; + /// Writes one or more user settings to settings.json, replacing each provided top-level key. A key whose value is null is removed. + /// Partial user settings to write, as a free-form object keyed by setting name. + /// The to monitor for cancellation requests. The default is . + public async Task SetAsync(object? settings, CancellationToken cancellationToken = default) + { + var request = new UserSettingsSetRequest { Settings = CopilotClient.ToJsonElementForWire(settings) ?? JsonElement.Parse("null") }; + await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.set", [request], cancellationToken); + } - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public GitHubTokenAcquireReason(string value) + /// Lists the top-level user-setting keys by who is allowed to govern them: `repo` keys a repository may override through `.github/copilot/settings.json`, `claudeRepo` keys a repository may override through a Claude-compatible config, and `managed` keys an enterprise policy may fix. The lists are static for a given runtime build and fail closed: a key absent from a list cannot be governed that way. + /// The to monitor for cancellation requests. The default is . + /// Top-level user-setting keys grouped by who may govern them. + internal async Task ListGovernanceKeysAsync(CancellationToken cancellationToken = default) { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.listGovernanceKeys", [], cancellationToken); } - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; + /// Atomically edits one canonical user setting inside its settings-file lock. Reads, canonicalizes touched aliases, applies the edit, validates the resulting top-level setting and writes before releasing the lock. An explicit autoUpdatesChannel set completes the staff migration in the same transaction. Only a malformed existing file returns `invalid`; refused keys, invalid values and I/O errors reject the call. + /// Nonempty canonical path segments of the setting to edit, starting with its top-level key. + /// Set a scalar, add or remove a string list item, or remove the setting. The handler rejects a missing or incompatible value. + /// Configuration directory to edit. Omitted or empty resolves the server's own home, as in `user.settings.loadForConfigDir`. + /// Required boolean, string or number for set, required string for add-item and remove-item, and forbidden for remove. The runtime rejects all other JSON values. + /// The to monitor for cancellation requests. The default is . + /// Outcome of an atomic user or repository settings edit. + internal async Task EditAsync(IList canonicalPath, SettingsEditOperation operation, string? configDir = null, object? value = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(canonicalPath); - /// The runtime is acquiring the registration's first credential. - public static GitHubTokenAcquireReason Initial { get; } = new("initial"); + var request = new UserSettingsEditRequest { CanonicalPath = canonicalPath, Operation = operation, ConfigDir = configDir, Value = CopilotClient.ToJsonElementForWire(value) }; + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.edit", [request], cancellationToken); + } - /// The runtime is replacing a credential that is approaching expiry. - public static GitHubTokenAcquireReason Refresh { get; } = new("refresh"); + /// Rewrites legacy and snake_case setting names in a settings object to their canonical spelling. A caller editing settings.json must normalize before writing: a legacy key left in place survives the write, is re-normalized on the next load, and silently shadows the canonical value the write just stored. + /// Free-form settings object, possibly using legacy or snake_case key names. + /// The to monitor for cancellation requests. The default is . + /// Settings object with canonical key names. + internal async Task NormalizeAsync(object? settings, CancellationToken cancellationToken = default) + { + var request = new UserSettingsNormalizeRequest { Settings = CopilotClient.ToJsonElementForWire(settings) ?? JsonElement.Parse("null") }; + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.normalize", [request], cancellationToken); + } - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(GitHubTokenAcquireReason left, GitHubTokenAcquireReason right) => left.Equals(right); + /// Diagnoses a set of already-loaded settings layers, reporting each key that will not take effect and why. Settings load tolerantly so that one bad key does not stop a session from starting; this method surfaces what was dropped so a caller can report it instead of silently ignoring the user's intent. Takes the layers as given rather than reading them, so a caller can diagnose exactly the content it is showing, including unsaved edits. Never echoes a setting's value, because a value may be a token or another secret. Marked internal because it exists to carry a CLI call site off the native addon, not to serve a consumer-facing need; it becomes public once an SDK consumer needs to inspect settings health itself. + /// Layers to diagnose, in the precedence order the caller applies them. + /// The to monitor for cancellation requests. The default is . + /// The settings that will not take effect across the given layers. + internal async Task DiagnoseProblemsAsync(IList? sources = null, CancellationToken cancellationToken = default) + { + var request = new UserSettingsProblemsRequest { Sources = sources }; + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.diagnoseProblems", [request], cancellationToken); + } - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(GitHubTokenAcquireReason left, GitHubTokenAcquireReason right) => !(left == right); + /// Validates one top-level setting subtree and writes just that key to settings.json as a single step. Doing both here closes the race a caller cannot close itself: between a separate preflight read and the write, the file may change. Rewriting only the touched key keeps legacy siblings out of settings.json. Answers a status rather than failing, so a validation problem and a write problem stay distinguishable. + /// Canonical path of the setting the caller is editing. Currently informational only. + /// Top-level setting name to write. + /// Value to write for the whole top-level key. Omitted when `delete` is true. Explicit null is preserved and validated as null. + /// True to remove the top-level key instead of writing it. + /// Records that the user chose an update channel explicitly, so the one-time staff channel migration stops reapplying. Honored only for the `autoUpdatesChannel` key. + /// The to monitor for cancellation requests. The default is . + /// Outcome of validating and writing one top-level setting subtree. + internal async Task ApplyTopKeyAsync(IList canonicalPath, string topKey, object? value = null, bool? delete = null, bool? completeStaffUpdateChannelMigration = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(canonicalPath); + ArgumentNullException.ThrowIfNull(topKey); - /// - public override bool Equals(object? obj) => obj is GitHubTokenAcquireReason other && Equals(other); + var request = new UserSettingsApplyTopKeyRequest { CanonicalPath = canonicalPath, TopKey = topKey, Value = CopilotClient.ToJsonElementForWire(value), Delete = delete, CompleteStaffUpdateChannelMigration = completeStaffUpdateChannelMigration }; + var payload = JsonSerializer.SerializeToNode(request, RpcJsonContext.Default.UserSettingsApplyTopKeyRequest)!.AsObject(); + if (request.Delete != true && request.Value is null) payload["value"] = null; + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.applyTopKey", [payload], cancellationToken); + } - /// - public bool Equals(GitHubTokenAcquireReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + /// Replaces the user settings file's entire content in one write. A key absent from `settings` is removed, which is what distinguishes this from `user.settings.set`: that one merges, so it can never clear a key the caller has dropped. For a caller saving the result of a raw edit, a merge would silently resurrect every key the user deleted. Records the same one-time mutation marks a per-key write records, so a setting the user has now chosen explicitly stops being migrated for them. + /// The settings object to store. It replaces the file's entire content, so a key absent here is removed. + /// The to monitor for cancellation requests. The default is . + internal async Task WriteAllAsync(object? settings, CancellationToken cancellationToken = default) + { + var request = new UserSettingsWriteAllRequest { Settings = CopilotClient.ToJsonElementForWire(settings) ?? JsonElement.Parse("null") }; + await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.writeAll", [request], cancellationToken); + } - /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + /// Merges a partial object into one top-level user setting, re-reading the stored value inside this runtime process's settings.json write lock so that concurrent writers in this process to different sub-fields of the same key do not clobber each other. Unlike `user.settings.set`, which replaces a key's whole value with what the caller last read, this keeps sub-fields the caller did not name when both the stored value and supplied value are objects; otherwise the supplied value replaces the stored value. Use it whenever two independent surfaces own different sub-fields of one setting. Returns the key's stored value after the write, in `value`. + /// Top-level user-setting key to merge into, such as `extensions`. + /// Value to store for `key`. When both the stored value and this value are objects, their fields are shallow-merged and fields this object does not name are preserved. Otherwise this value replaces the stored value. + /// Copilot configuration directory to write the settings document in, taking precedence over the server's own `COPILOT_HOME` and default home. Omit it, or pass an empty string, to use the directory the server resolved for itself. + /// Single-component name for the settings document at `<configDir>/settings/<subDir>.json`; path separators, `.` and `..` are rejected. Omit it, or pass an empty string, to use the main settings document. + /// The to monitor for cancellation requests. The default is . + /// The stored value of the user setting after the merge. + internal async Task UpdateAsync(string key, object? value, string? configDir = null, string? subDir = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(key); - /// - public override string ToString() => Value; + var request = new UserSettingsUpdateRequest { Key = key, Value = CopilotClient.ToJsonElementForWire(value) ?? JsonElement.Parse("null"), ConfigDir = configDir, SubDir = subDir }; + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.update", [request], cancellationToken); + } - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter + /// Reads settings.json as stored for a caller that is about to edit it. Unlike `user.settings.get`, this returns file values rather than per-key metadata and defaults. Reports `missing` for an absent file and `invalid` when the existing file will not parse. Retired config.json and extensionless files are not read. + /// The to monitor for cancellation requests. The default is . + /// Contents of settings.json as stored, for a caller about to rewrite it. + internal async Task LoadForEditAsync(CancellationToken cancellationToken = default) { - /// - public override GitHubTokenAcquireReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.loadForEdit", [], cancellationToken); + } - /// - public override void Write(Utf8JsonWriter writer, GitHubTokenAcquireReason value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(GitHubTokenAcquireReason)); - } + /// Validates one top-level setting subtree without writing it, reporting each problem with the path it applies to. Scoped to a single top-level key on purpose: validating the whole file would reject an edit whenever an unrelated pre-existing field is malformed, locking the user out of the very tool that could fix it. + /// Top-level setting name to validate. + /// Value to validate. Omitted when `delete` is true. + /// True to validate a removal of this key. The current settings file is not read. + /// The to monitor for cancellation requests. The default is . + /// Outcome of validating one top-level setting subtree. + internal async Task ValidateAsync(string topKey, object? value = null, bool? delete = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(topKey); + + var request = new UserSettingsValidateRequest { TopKey = topKey, Value = CopilotClient.ToJsonElementForWire(value), Delete = delete }; + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.validate", [request], cancellationToken); } } - -/// Explicit user decisions, never inferred from a permission grant or model response. +/// Provides server-scoped Repo APIs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct InstallationDecision : IEquatable +public sealed class ServerRepoApi { - private readonly string? _value; + private readonly JsonRpc _rpc; - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public InstallationDecision(string value) + internal ServerRepoApi(JsonRpc rpc) { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; + _rpc = rpc; } - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// The user explicitly approved the exact review on this request. - public static InstallationDecision Confirm { get; } = new("confirm"); + /// Settings APIs. + public ServerRepoSettingsApi Settings => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; +} - /// The user declined the reviewed operation. - public static InstallationDecision Decline { get; } = new("decline"); +/// Provides server-scoped RepoSettings APIs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ServerRepoSettingsApi +{ + private readonly JsonRpc _rpc; - /// The user cancelled the pending decision without granting consent. - public static InstallationDecision Cancel { get; } = new("cancel"); + internal ServerRepoSettingsApi(JsonRpc rpc) + { + _rpc = rpc; + } - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(InstallationDecision left, InstallationDecision right) => left.Equals(right); + /// Atomically edits one allowed top-level repository setting. Reads the raw file, preserves unrelated keys, applies one scalar or list edit, validates the result and writes under one file lock, so concurrent sibling or list edits are not lost. A malformed or unsafe existing file returns `invalid` without a write; rejected keys, invalid values and I/O errors reject the call. + /// Absolute path of the repository root. + /// Shared or local repository settings file. + /// One canonical top-level repository setting that the allowlist permits. + /// Set a scalar, add or remove a string list item, or remove the setting. The handler rejects a missing or incompatible value. + /// Required boolean, string or number for set, required string for add-item and remove-item, and forbidden for remove. The runtime rejects all other JSON values. + /// The to monitor for cancellation requests. The default is . + /// Outcome of an atomic user or repository settings edit. + internal async Task EditAsync(string repoRoot, RepoSettingsEditScope scope, string key, SettingsEditOperation operation, object? value = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(repoRoot); + ArgumentNullException.ThrowIfNull(key); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(InstallationDecision left, InstallationDecision right) => !(left == right); + var request = new RepoSettingsEditRequest { RepoRoot = repoRoot, Scope = scope, Key = key, Operation = operation, Value = CopilotClient.ToJsonElementForWire(value) }; + return await CopilotClient.InvokeRpcAsync(_rpc, "repo.settings.edit", [request], cancellationToken); + } - /// - public override bool Equals(object? obj) => obj is InstallationDecision other && Equals(other); + /// Reads one repository settings file without settings normalization or layering. The existing JSONC parser accepts comments and trailing commas and applies its prototype-safety rules. Missing or blank files return an empty valid config. Invalid files return an empty config with invalid=true; other read failures reject. + /// Absolute path of the repository root. + /// Use repo for shared settings or local for settings.local.json. Other scopes are rejected. + /// The to monitor for cancellation requests. The default is . + /// Parsed repository settings without normalization or layering. + internal async Task ReadRawAsync(string repoRoot, string scope, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(repoRoot); + ArgumentNullException.ThrowIfNull(scope); - /// - public bool Equals(InstallationDecision other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + var request = new RepoSettingsReadRawRequest { RepoRoot = repoRoot, Scope = scope }; + return await CopilotClient.InvokeRpcAsync(_rpc, "repo.settings.readRaw", [request], cancellationToken); + } - /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + /// Writes or removes one allowed top-level repository setting through the existing validator and file lock. Preserves unrelated stored keys. Invalid keys, values, existing documents, and I/O errors reject without replacing the file. Deletion requires delete=true; otherwise value must be present. + /// Absolute path of the repository root. + /// Use repo for shared settings or local for settings.local.json. Other scopes are rejected. + /// Canonical top-level key permitted by the repository settings allowlist. + /// Value to validate and write. Required unless delete is true; a null value is validated, not treated as deletion. + /// True to remove the key instead of writing it. + /// The to monitor for cancellation requests. The default is . + internal async Task WriteKeyAsync(string repoRoot, string scope, string key, object? value = null, bool? delete = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(repoRoot); + ArgumentNullException.ThrowIfNull(scope); + ArgumentNullException.ThrowIfNull(key); - /// - public override string ToString() => Value; + var request = new RepoSettingsWriteKeyRequest { RepoRoot = repoRoot, Scope = scope, Key = key, Value = CopilotClient.ToJsonElementForWire(value), Delete = delete }; + var payload = JsonSerializer.SerializeToNode(request, RpcJsonContext.Default.RepoSettingsWriteKeyRequest)!.AsObject(); + if (request.Delete != true && request.Value is null) payload["value"] = null; + await CopilotClient.InvokeRpcAsync(_rpc, "repo.settings.writeKey", [payload], cancellationToken); + } - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter + /// Replaces the repository-overridable settings in one locked write. Allowed keys present in settings are validated and written; allowed keys absent from settings are removed. Other input keys are ignored, and unknown keys already stored in the file remain unchanged. Invalid values or an invalid existing file reject without a partial write. + /// Absolute path of the repository root. + /// Use repo for shared settings or local for settings.local.json. Other scopes are rejected. + /// Object of allowed settings to store. Missing allowlisted keys are removed. Other input keys are ignored, while unknown stored keys are preserved. + /// The to monitor for cancellation requests. The default is . + internal async Task WriteAllAsync(string repoRoot, string scope, object? settings, CancellationToken cancellationToken = default) { - /// - public override InstallationDecision Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } + ArgumentNullException.ThrowIfNull(repoRoot); + ArgumentNullException.ThrowIfNull(scope); - /// - public override void Write(Utf8JsonWriter writer, InstallationDecision value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(InstallationDecision)); - } + var request = new RepoSettingsWriteAllRequest { RepoRoot = repoRoot, Scope = scope, Settings = CopilotClient.ToJsonElementForWire(settings) ?? JsonElement.Parse("null") }; + await CopilotClient.InvokeRpcAsync(_rpc, "repo.settings.writeAll", [request], cancellationToken); } } - -/// Provides server-scoped RPC methods (no session required). -public sealed class ServerRpc +/// Provides server-scoped FeatureFlags APIs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ServerFeatureFlagsApi { private readonly JsonRpc _rpc; - internal ServerRpc(JsonRpc rpc) + internal ServerFeatureFlagsApi(JsonRpc rpc) { _rpc = rpc; } - /// Checks server responsiveness and returns protocol information. - /// Optional message to echo back. + /// Resolves named gates against this installation's cached online-experiment assignment, for the account the runtime is already authenticated as. Unlike `featureFlags.resolve`, which layers static availability with the populations supplied, this reads the experiment assignment the runtime cached for its own current authentication, so a launch can honor a rollout before any session exists. The account is never taken from the request: naming one would read another account's cached assignments. The cached assignment answers immediately and any network refresh happens behind it, so this call does not wait on the network. Not available to an extension, which cannot carry the user's consent to read their assignment. + /// The feature flags to resolve. A name this runtime does not declare is reported as an error rather than as a disabled flag. + /// Whether the caller is GitHub or Microsoft staff. + /// Whether the caller is in an experimental population. + /// Whether the caller belongs to a team plan. + /// Whether streamer mode is on, which withholds the populations an experiment would otherwise segment on. + /// Whether the runtime may refresh the cached assignment over the network without an active session. The refresh never blocks this call: the answer comes from the cache either way, so a refresh only affects a later launch. + /// GitHub-staff membership. Omit when it was never recorded: a legacy staff account then counts as both staff populations and keeps every staff feature. + /// Microsoft-staff membership. Omitted the same way as `isStaffGitHub`. + /// Environment to read flag overrides from. Defaults to this runtime process's own environment. + /// User settings document whose `featureFlags` block is layered on top of the resolved set. + /// When this installation first launched, in milliseconds since the Unix epoch. Experiments that segment on installation age read it. Omit when the first launch was never recorded. /// The to monitor for cancellation requests. The default is . - /// Server liveness response, including the echoed message, current server timestamp, and protocol version. - [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] - public async Task PingAsync(string? message = null, CancellationToken cancellationToken = default) + /// The resolved gates, keyed by flag name. + internal async Task> ResolveStartupGatesAsync(IList flags, bool isStaff, bool isExperimental, bool isTeam, bool streamerMode, bool refreshWithoutSession, bool? isStaffGithub = null, bool? isStaffMicrosoft = null, IDictionary? environment = null, object? config = null, double? firstLaunchAtMillis = null, CancellationToken cancellationToken = default) { - var request = new PingRequest { Message = message }; - return await CopilotClient.InvokeRpcAsync(_rpc, "ping", [request], cancellationToken); + ArgumentNullException.ThrowIfNull(flags); + + var request = new FeatureFlagsResolveStartupGatesRequest { Flags = flags, IsStaff = isStaff, IsExperimental = isExperimental, IsTeam = isTeam, StreamerMode = streamerMode, RefreshWithoutSession = refreshWithoutSession, IsStaffGitHub = isStaffGithub, IsStaffMicrosoft = isStaffMicrosoft, Environment = environment, Config = CopilotClient.ToJsonElementForWire(config), FirstLaunchAtMillis = firstLaunchAtMillis }; + return await CopilotClient.InvokeRpcAsync>(_rpc, "featureFlags.resolveStartupGates", [request], cancellationToken); } - /// Performs the SDK server connection handshake and validates the optional connection token. Marked internal because this is JSON-RPC transport plumbing invoked automatically by an SDK client's own `connect()` wrapper, not a user-facing method. Stays internal as long as the SDK client owns the handshake; would only become public if the SDK ever exposed the raw schema surface to consumers without a connection wrapper. - /// Opt this connection in to GitHub telemetry forwarding for its lifetime. When set, the runtime forwards this host's telemetry across all its sessions, its sessionless events, and explicitly process-wide events over the `gitHubTelemetry.event` notification. Connections intentionally sharing one server receive that server's events; independently embedded runtime hosts do not receive each other's host-owned events. Regular events are also written to the runtime's normal GitHub/CTS path (dual-write); host-only compatibility events are forward-only and intentionally skip that path. Intended for first-party hosts that re-emit the events into their own telemetry stores. Both unrestricted and restricted events are forwarded, each tagged with a `restricted` discriminator; a backstop drops restricted events when restricted telemetry is disabled — using the process-global gate for ordinary events and an explicit session-scoped decision for host-only events. - /// Identity of the integrating host. Optional; omit it to keep the default attribution. - /// Task kinds this connection can decode when observing session tasks. Omit to retain agent and shell compatibility. - /// Connection token; required when the server was started with COPILOT_CONNECTION_TOKEN. + /// Lists every feature flag this runtime declares, with the population each one is declared for, its experimental description, and the online-experiment flag it maps to. This is the static catalog: it describes how each flag is defined, not whether it is on for anyone. Use `featureFlags.resolve` for static values with caller overrides, not active runtime values or experiment assignments. /// The to monitor for cancellation requests. The default is . - /// Handshake result reporting the server's protocol version and package version on success. - [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] - internal async Task ConnectAsync(bool? enableGitHubTelemetryForwarding = null, ConnectClientInfo? clientInfo = null, IList? supportedTaskKinds = null, string? token = null, CancellationToken cancellationToken = default) + /// Every feature flag this runtime declares, with the shape each one was declared with. + internal async Task ListMetadataAsync(CancellationToken cancellationToken = default) { - var request = new ConnectRequest { EnableGitHubTelemetryForwarding = enableGitHubTelemetryForwarding, ClientInfo = clientInfo, SupportedTaskKinds = supportedTaskKinds, Token = token }; - return await CopilotClient.InvokeRpcAsync(_rpc, "connect", [request], cancellationToken); + return await CopilotClient.InvokeRpcAsync(_rpc, "featureFlags.listMetadata", [], cancellationToken); } - /// Registers the calling SDK client as the per-entrypoint extension launch provider. Call before creating any sessions. When omitted, the runtime uses its built-in extension launcher. + /// Computes static feature flags from the supplied population and overrides without changing runtime state or verifying audience membership. Starts from declared availability, then applies direct feature-flag environment values, user settings, and explicit `flagOverrides`. It does not apply `COPILOT_EXP_*` experiment assignments, authenticated startup gates, or active runtime state. Settings include `enabledFeatureFlags`, the legacy `feature_flags.enabled` list, and supported convenience settings. Supply `baseFlags` to use an existing flag set instead of declared availability. Omit `isStaffGitHub` or `isStaffMicrosoft` when membership was never recorded: a legacy staff account then counts as both populations. + /// Whether the caller is GitHub or Microsoft staff. + /// Whether the caller is in an experimental population. + /// Whether the caller belongs to the team population used by feature availability. + /// GitHub-staff membership. Omit when it was never recorded: a legacy staff account then counts as both staff populations and keeps every staff feature. + /// Microsoft-staff membership. Omitted the same way as `isStaffGitHub`. + /// Resolve against this already-computed flag set rather than each flag's declared availability. Keyed by flag name. + /// Environment for direct feature-flag overrides and `COPILOT_CLI_ENABLED_FEATURE_FLAGS`. Does not apply `COPILOT_EXP_*` experiment assignments. Defaults to this runtime process's own environment. + /// User settings to apply, including `enabledFeatureFlags`, legacy `feature_flags.enabled`, and supported convenience settings. + /// Flags forced to a value after every other layer, keyed by flag name. /// The to monitor for cancellation requests. The default is . - [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] - public async Task RegisterExtensionLaunchProviderAsync(CancellationToken cancellationToken = default) + /// The resolved feature flags, keyed by flag name. + internal async Task> ResolveAsync(bool isStaff, bool isExperimental, bool isTeam, bool? isStaffGithub = null, bool? isStaffMicrosoft = null, IDictionary? baseFlags = null, IDictionary? environment = null, object? config = null, IDictionary? flagOverrides = null, CancellationToken cancellationToken = default) { - await CopilotClient.InvokeRpcAsync(_rpc, "registerExtensionLaunchProvider", [], cancellationToken); + var request = new FeatureFlagsResolveRequest { IsStaff = isStaff, IsExperimental = isExperimental, IsTeam = isTeam, IsStaffGitHub = isStaffGithub, IsStaffMicrosoft = isStaffMicrosoft, BaseFlags = baseFlags, Environment = environment, Config = CopilotClient.ToJsonElementForWire(config), FlagOverrides = flagOverrides }; + return await CopilotClient.InvokeRpcAsync>(_rpc, "featureFlags.resolve", [request], cancellationToken); } +} - /// Environments APIs. - public ServerEnvironmentsApi Environments => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; +/// Provides server-scoped Permissions APIs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ServerPermissionsApi +{ + private readonly JsonRpc _rpc; - /// Host APIs. - public ServerHostApi Host => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; + internal ServerPermissionsApi(JsonRpc rpc) + { + _rpc = rpc; + } - /// Hooks APIs. - public ServerHooksApi Hooks => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; + /// Interprets a verbatim managed `permissions` slice into the three policy questions a host asks before granting elevated permissions: whether bypass-permissions mode is forbidden, whether a policy exists that this build could not determine, and whether the layer supplies permission rules the session must enforce. An absent or empty slice means no managed policy applies. The runtime and its permission service share one policy type, so a new managed permission setting is interpreted in exactly one place; a host that reimplements the reading would silently diverge the first time that set grows. Hosts must fail closed on an undetermined policy rather than treating it as permission granted. + /// The `permissions` object from a managed settings layer, for example `{"disableBypassPermissionsMode":"disable"}`. Omitted or null means no managed policy applies. + /// The to monitor for cancellation requests. The default is . + /// The policy questions a host asks before granting elevated permissions. + internal async Task EvaluateManagedPolicyAsync(object? permissions = null, CancellationToken cancellationToken = default) + { + var request = new PermissionsEvaluateManagedPolicyParams { Permissions = CopilotClient.ToJsonElementForWire(permissions) }; + return await CopilotClient.InvokeRpcAsync(_rpc, "permissions.evaluateManagedPolicy", [request], cancellationToken); + } - /// Models APIs. - public ServerModelsApi Models => + /// Locations APIs. + public ServerPermissionsLocationsApi Locations => field ?? Interlocked.CompareExchange(ref field, new(_rpc), null) ?? field; - /// Sandbox APIs. - public ServerSandboxApi Sandbox => + /// FolderTrust APIs. + public ServerPermissionsFolderTrustApi FolderTrust => field ?? Interlocked.CompareExchange(ref field, new(_rpc), null) ?? field; +} - /// Tools APIs. - public ServerToolsApi Tools => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; +/// Provides server-scoped PermissionsLocations APIs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ServerPermissionsLocationsApi +{ + private readonly JsonRpc _rpc; - /// Account APIs. - public ServerAccountApi Account => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; + internal ServerPermissionsLocationsApi(JsonRpc rpc) + { + _rpc = rpc; + } - /// Secrets APIs. - public ServerSecretsApi Secrets => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; + /// Drops stored per-location permission grants whose directory no longer exists, and reports which location keys were removed. Runs against the permission store under the runtime's own Copilot home, the same store `session.permissions.locations.resolve` reads. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract; it is migration plumbing, not a surface consumers are meant to depend on. It can become public once an SDK consumer needs to prune its own permission store. + /// The to monitor for cancellation requests. The default is . + /// Which stored location permissions were dropped because their directory is gone. + internal async Task CleanupOrphanedAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "permissions.locations.cleanupOrphaned", [], cancellationToken); + } +} - /// Mcp APIs. - public ServerMcpApi Mcp => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; +/// Provides server-scoped PermissionsFolderTrust APIs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ServerPermissionsFolderTrustApi +{ + private readonly JsonRpc _rpc; - /// Extensions APIs. - public ServerExtensionsApi Extensions => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; + internal ServerPermissionsFolderTrustApi(JsonRpc rpc) + { + _rpc = rpc; + } - /// Skills APIs. - public ServerSkillsApi Skills => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; + /// Reports whether a folder is trusted, reading the persisted trusted-folders list directly rather than through a session. Use this from a host that has no session yet, such as a one-shot command deciding whether to honor a checkout's own settings. It differs from the session-scoped check in two ways that matter: it takes the configuration directory to read the list from, instead of inheriting the session's, and it answers only from the persisted list, so trust a user granted for the lifetime of one session is not visible here. A host that has a session should prefer the session-scoped check, which also covers session-only trust. + /// Folder to check. A folder counts as trusted when it is itself listed, when an ancestor is listed, or when its git root is listed. + /// Configuration directory to read the trusted-folders list from. Omit it to read the default location; supply it when the host has redirected its configuration, or the check answers from a list the host is not using. + /// The to monitor for cancellation requests. The default is . + /// Folder trust check result. + internal async Task CheckTrustAsync(string path, string? configDir = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(path); - /// Catalog APIs. - public ServerCatalogApi Catalog => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; + var request = new FolderTrustIsTrustedParams { Path = path, ConfigDir = configDir }; + return await CopilotClient.InvokeRpcAsync(_rpc, "permissions.folderTrust.checkTrust", [request], cancellationToken); + } +} - /// Plugins APIs. - public ServerPluginsApi Plugins => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; +/// Provides server-scoped ManagedSettings APIs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ServerManagedSettingsApi +{ + private readonly JsonRpc _rpc; - /// Agents APIs. - public ServerAgentsApi Agents => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; + internal ServerManagedSettingsApi(JsonRpc rpc) + { + _rpc = rpc; + } - /// Instructions APIs. - public ServerInstructionsApi Instructions => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; + /// Discovers device-managed settings the way the runtime's own device layer does, and reports the sandbox floor and the load outcome alongside the validated document. This differs from `managedSettings.read` in three ways a policy-enforcing host depends on: a document that validates to an empty object is reported as absent rather than as an empty policy, a discovery error does not suppress a document that still validates, and a failure is reported as a flag rather than only as error text. Hosts that compose the device layer into an effective policy use this, because `managedSettings.read` cannot distinguish "no device policy" from "device policy could not be read" and would silently hand the user's own layer the win. + /// The to monitor for cancellation requests. The default is . + /// The device-managed settings layer, with the sandbox floor and the load outcome a host needs to compose it into an effective policy. + internal async Task ReadDeviceAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.readDevice", [], cancellationToken); + } - /// GlobalState APIs. - public ServerGlobalStateApi GlobalState => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; + /// Retains the supplied managed layers as this runtime process's startup policy and re-applies the result to every live session. Hosts that own their own short-lived process policy use this, so a guarded mutation is checked against the policy the host actually fetched. Retaining is a whole-process replacement rather than a merge, so a host that already resolved a good policy must not retain a partial one over it. The failure flags are honest inputs rather than assumptions: reporting a fetch or load that failed keeps the layers already retained, and the interim fail-closed state, instead of clearing them. + /// Whether a device policy source demonstrably exists. This, not a read error, is the evidence a device is managed, so a fail-closed policy gates on it. + /// Whether any managed policy source exists, independently of fetch failures. + /// Whether the server policy source exists, even when its response is unavailable. + /// Whether a required fresh server-policy confirmation failed. + /// Preserve the retained server layer during a device-only resolution. + /// Whether the device sandbox intent is unreadable independently of its other settings. + /// Whether the shared sandbox policy is undetermined. Diagnostic errors alone do not imply this flag. + /// Whether permission policy is undetermined. Sandbox-only uncertainty does not imply this flag. + /// Whether uncertainty alone enabled the shared sandbox floor. + /// Whether the server fetch failed. Report it honestly: true keeps the already-retained server layer rather than replacing it with an absent one. + /// Whether the device load failed. Report it honestly, for the same reason as `serverFetchFailed`. + /// The resolved server policy. A known source can lack a response after a forced-refresh failure. + /// The device/MDM policy layer. + /// Sandbox floor declared by the device layer, carried separately so a document that failed validation can still raise the floor. + /// Shared server/device sandbox floor, excluding session-local helper policy. + /// The policy helper's response: the third managed layer that a process owning its startup policy resolves. It is retained with the other two so that every ambient read in this process, including the mutation guards and the governed marketplace allowlist that the `plugins.*` handlers derive, sees the whole policy the process fetched. A session-owning process omits it, because it resolves a helper per session against that session's working directory. + /// The to monitor for cancellation requests. The default is . + internal async Task RetainPolicyAsync(bool deviceSourceFound, bool managedPolicyPresent, bool serverSourceFound, bool serverForcedRefreshFailure, bool preserveServerState, bool deviceSandboxUndetermined, bool sandboxFloorUndetermined, bool permissionPolicyUndetermined, bool sandboxEnabledByUndeterminedPolicy, bool serverFetchFailed, bool deviceLoadFailed, object? serverResponse = null, object? deviceResponse = null, object? deviceSandboxFloor = null, object? sharedSandboxFloor = null, object? policyHelperResponse = null, CancellationToken cancellationToken = default) + { + var request = new ManagedSettingsRetainPolicyParams { DeviceSourceFound = deviceSourceFound, ManagedPolicyPresent = managedPolicyPresent, ServerSourceFound = serverSourceFound, ServerForcedRefreshFailure = serverForcedRefreshFailure, PreserveServerState = preserveServerState, DeviceSandboxUndetermined = deviceSandboxUndetermined, SandboxFloorUndetermined = sandboxFloorUndetermined, PermissionPolicyUndetermined = permissionPolicyUndetermined, SandboxEnabledByUndeterminedPolicy = sandboxEnabledByUndeterminedPolicy, ServerFetchFailed = serverFetchFailed, DeviceLoadFailed = deviceLoadFailed, ServerResponse = CopilotClient.ToJsonElementForWire(serverResponse), DeviceResponse = CopilotClient.ToJsonElementForWire(deviceResponse), DeviceSandboxFloor = CopilotClient.ToJsonElementForWire(deviceSandboxFloor), SharedSandboxFloor = CopilotClient.ToJsonElementForWire(sharedSandboxFloor), PolicyHelperResponse = CopilotClient.ToJsonElementForWire(policyHelperResponse) }; + await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.retainPolicy", [request], cancellationToken); + } - /// Commands APIs. - public ServerCommandsApi Commands => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; + /// Stamps an effective managed policy's individually-controlled entries onto a user settings document, returning the settings a host should actually act on. The managed layer wins on every plugin spec and marketplace name it names, and leaves everything it does not name as the user wrote it. Hosts use this rather than merging the two documents themselves, because the merge is per entry rather than per document and a whole-document overlay would either discard the user's unmanaged entries or let a user entry outrank a managed one. Omitting the managed layer returns the settings unchanged, which is what an unmanaged host wants. Rejects when either document is present but is not a JSON object rather than treating it as absent, since silently dropping a malformed policy hands the user's layer the win on exactly the entries the policy exists to control. + /// The user settings document to stamp. Entries the managed layer does not name are returned exactly as supplied. + /// The effective managed policy, as composed by `managedSettings.resolveEffectiveValue`. Omit it on an unmanaged host: the settings then come back unchanged. + /// The to monitor for cancellation requests. The default is . + /// The user settings with the managed layer's controlled entries stamped on. + internal async Task ApplyControlsAsync(object? settings, object? managed = null, CancellationToken cancellationToken = default) + { + var request = new ManagedSettingsApplyControlsParams { Settings = CopilotClient.ToJsonElementForWire(settings) ?? JsonElement.Parse("null"), Managed = CopilotClient.ToJsonElementForWire(managed) }; + return await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.applyControls", [request], cancellationToken); + } - /// User APIs. - public ServerUserApi User => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; + /// Validates the managed settings a client is supplying for a session it is about to open, and returns them reduced to the keys a client is allowed to set. A client-supplied policy is not the same thing as an administrator's: it may only narrow permissions, so this accepts `permissions` and rejects every other key outright rather than ignoring it. Rejecting is the point — a host that silently dropped an unsupported key would report a policy as applied while running without it. Hosts call this before opening the session rather than letting the open fail, so a malformed policy is reported against the request that carried it. The runtime applies the same rule again when the session starts, so this cannot be used to smuggle a wider document past it. An accepted document that sets nothing comes back as an empty object, which is distinct from supplying nothing at all. + /// The managed settings a client is supplying for a session it is about to open. Omit them when the client is imposing no policy of its own. + /// The to monitor for cancellation requests. The default is . + /// The accepted managed settings, reduced to the keys a client is allowed to set. + internal async Task ParseClientValueAsync(object? settings = null, CancellationToken cancellationToken = default) + { + var request = new ManagedSettingsParseClientValueParams { Settings = CopilotClient.ToJsonElementForWire(settings) }; + return await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.parseClientValue", [request], cancellationToken); + } - /// GitHubRepository APIs. - public ServerGitHubRepositoryApi GitHubRepository => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; + /// Composes the supplied server, device/MDM, device sandbox floor and policy-helper layers into the single effective managed policy a host should enforce, resolving each key across the layers by the runtime's own precedence rules. Hosts that assemble managed layers themselves use this so their precedence matches the runtime's exactly instead of reimplementing it. The composition is not a pure function of its inputs: an advisory sandbox force-on that arrived over the server channel is declined when this host cannot run a sandbox, so the result reflects the runtime host's capabilities, and the returned settings carry `sandboxAdvisoryFloorDeclined: true` when that happened. Enforced channels (device/MDM and policy helper) are never declined, and a server force-on carrying `failIfUnavailable` is preserved so the session fails loudly instead of quietly running unsandboxed. Rejects when a supplied layer is present but is not a JSON object, rather than treating it as unmanaged, because silently dropping a malformed policy would hand the user's own layer the win on exactly the keys the policy exists to control. + /// The account's server-delivered org policy, as returned by a managed-settings fetch. Omitted before sign-in or when the fetch has not landed. + /// The device layer: native MDM and managed-file settings describing the machine rather than the account. + /// The device sandbox floor, folded into the device layer under `sandbox` before composition. + /// The policy-helper layer, an enforced channel sourced from a platform policy helper process. + /// The to monitor for cancellation requests. The default is . + /// The effective managed policy composed from the supplied layers. + internal async Task ResolveEffectiveValueAsync(object? server = null, object? mdm = null, object? deviceSandboxFloor = null, object? policyHelper = null, CancellationToken cancellationToken = default) + { + var request = new ManagedSettingsResolveEffectiveValueParams { Server = CopilotClient.ToJsonElementForWire(server), Mdm = CopilotClient.ToJsonElementForWire(mdm), DeviceSandboxFloor = CopilotClient.ToJsonElementForWire(deviceSandboxFloor), PolicyHelper = CopilotClient.ToJsonElementForWire(policyHelper) }; + return await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.resolveEffectiveValue", [request], cancellationToken); + } - /// GitHubOwners APIs. - public ServerGitHubOwnersApi GitHubOwners => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; + /// Decides what a host must do to its bypass-permissions state after a managed-policy resolution lands, given the state already in force and the account now signed in. A host holds two pieces of state — which layer disabled bypass mode, and for a server-delivered disable, the account it was pinned to — and cannot decide the next step from the new policy alone. Three rules make that so, and each one is a control rather than a preference. A device/MDM disable outranks the account, so a later server answer never lifts it. An unknown policy, which is what a failed fetch or a refusal this build cannot enforce yields, preserves whatever is in force instead of reverting to permissive. And a server disable is lifted only when the account it was pinned to is no longer the signed-in one, which is what makes signing in to an unrestricted account recover without a restart while a reconnect on the same restricted account does not. Hosts call this rather than reimplementing it because getting the pinning backwards either strands a disable on an account that never carried one or drops one that still applies. `setDisabledState` is omitted when the host must leave its stored flag untouched, which is distinct from being told to set it to false. + /// The account signed in now. A `managed` disable pinned to a different account is lifted. + /// What the resolution said about bypass mode. Send `unknown` rather than omitting the field when there is no verdict. + /// Which layer currently disables bypass mode. Omit it when nothing does. + /// The account a `managed` disable is pinned to. Omit it when the current source is not `managed`; it is ignored for the other sources. + /// The to monitor for cancellation requests. The default is . + /// The bypass-permissions state a host should hold after the resolution, and what it must do to get there. + internal async Task ResolveBypassDisableTransitionAsync(string activeIdentity, ManagedSettingsBypassPolicyResolution policy, ManagedSettingsBypassDisableSource? currentSource = null, string? currentManagedIdentity = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(activeIdentity); - /// Git APIs. - public ServerGitApi Git => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; + var request = new ManagedSettingsResolveBypassDisableTransitionParams { ActiveIdentity = activeIdentity, Policy = policy, CurrentSource = currentSource, CurrentManagedIdentity = currentManagedIdentity }; + return await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.resolveBypassDisableTransition", [request], cancellationToken); + } - /// ManagedSettings APIs. - public ServerManagedSettingsApi ManagedSettings => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; + /// Resolves managed (enterprise policy) settings for a host from every layer at once: the account's server-delivered policy, the device/MDM layer, and a platform policy helper. Hosts use this instead of fetching and composing the layers themselves, because the per-layer precedence and the fail-closed rules that apply when a layer cannot be confirmed are the runtime's own and must match the policy a session will later enforce. It does not retain or apply anything to process-global policy state, so a host may call it before any session exists — which is the point, since plugin activation and one-shot commands resolve policy before a session is available to carry it. The returned per-layer outcome flags are load-bearing rather than diagnostic: a host must distinguish an unmanaged device from a managed device whose policy could not be confirmed, and apply its fail-closed gates in the second case. Reports its diagnostics in `logMessages` rather than logging them, so a host that runs before a log sink exists still sees them. + /// User agent to send with the policy fetch. + /// The host's environment variables, read for cache location and policy overrides. Supplied by the host rather than read from the runtime process so a command that selected its own configuration home is honored. + /// The host's platform, in Node's `process.platform` spelling, used to locate platform-specific cache and policy paths. + /// The host's home directory, used to resolve cache and managed-file locations. + /// The policy-helper invocation reason. Defaults to sessionStart. + /// Whether to run ingestion. Omit or set false to return an empty result without discovering or fetching policy. + /// The authenticated identity to resolve policy for. Omitted before sign-in, in which case no server layer is fetched. + /// The session whose credential store and working directory the policy helper should use. Omit it when no session exists yet and the process-wide store applies. + /// A device layer supplied by the host instead of discovered from the machine. Pair it with `hasDeviceResponse` so an explicit absence is distinguishable from an omitted override. + /// Whether `deviceResponse` is an override at all. True with a null `deviceResponse` asserts the device layer is empty; false discovers it from the machine. + /// Whether to ignore a cached server policy when reading, forcing the resolution to come from a live fetch. + /// How this ingestion may use the on-disk server-policy cache. Defaults to the runtime's own policy when omitted. + /// Whether a configured platform policy helper may be run to produce an enforced helper layer. + /// Directory the policy helper runs in when this ingestion is not owned by a registered session. A host resolving policy before its session exists must pass this, or a directory-sensitive helper judges the wrong repository. + /// Name of the embedding client, reported to the managed-settings service as `client_id` so it can refuse to deliver a policy this client is too old to enforce. Omit it and the runtime reports a generic identity; the runtime version sent alongside is the gate that actually matters. + /// The to monitor for cancellation requests. The default is . + /// The managed policy layers an ingestion resolved, together with the per-layer outcome flags a host needs to decide how much to trust the result. The flags are reported rather than folded into the settings because a host must be able to distinguish an unmanaged device from a managed device whose policy could not be confirmed, and apply its fail-closed gates in the second case. + internal async Task IngestAsync(string userAgent, object? environment, string platform, string homeDirectory, ManagedSettingsPolicyHelperReason? policyHelperReason = null, bool? selfFetch = null, object? authInfo = null, string? sessionId = null, object? deviceResponse = null, bool? hasDeviceResponse = null, bool? bypassCacheRead = null, ManagedSettingsServerPolicyCacheMode? serverPolicyCacheMode = null, bool? policyHelperEnabled = null, string? policyHelperWorkingDirectory = null, string? clientName = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(userAgent); + ArgumentNullException.ThrowIfNull(platform); + ArgumentNullException.ThrowIfNull(homeDirectory); - /// Runtime APIs. - public ServerRuntimeApi Runtime => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; + var request = new ManagedSettingsIngestParams { UserAgent = userAgent, Environment = CopilotClient.ToJsonElementForWire(environment) ?? JsonElement.Parse("null"), Platform = platform, HomeDirectory = homeDirectory, PolicyHelperReason = policyHelperReason, SelfFetch = selfFetch, AuthInfo = CopilotClient.ToJsonElementForWire(authInfo), SessionId = sessionId, DeviceResponse = CopilotClient.ToJsonElementForWire(deviceResponse), HasDeviceResponse = hasDeviceResponse, BypassCacheRead = bypassCacheRead, ServerPolicyCacheMode = serverPolicyCacheMode, PolicyHelperEnabled = policyHelperEnabled, PolicyHelperWorkingDirectory = policyHelperWorkingDirectory, ClientName = clientName }; + return await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.ingest", [request], cancellationToken); + } + + /// Resolves the managed (enterprise policy) settings a host must enforce from startup, and judges the account against the enterprise sign-in pin in the same pass. This composes the layers the way `managedSettings.ingest` does and then answers the two questions a host cannot answer for itself: which fail-closed floors apply when a layer could not be confirmed, and whether this credential is allowed to sign in at all. The sign-in verdict is the reason a host must not assemble this itself from `managedSettings.ingest` — a pinned organization is an access control, and a host that composed the layers but skipped the pin would let a developer sign in with a personal or another enterprise's account and escape every control that keys off the signed-in identity. The verdict is computed on every resolution, including one that is not retained, so declining to apply policy never widens access. Set `retainAndApply` to keep the resolved policy as this runtime process's startup policy; leave it false to resolve without side effects. + /// User agent to send with the policy fetch. + /// The host's environment variables, read for cache location, policy overrides, and the variables that would make a membership snapshot forgeable. + /// The host's platform, in Node's `process.platform` spelling, used to locate platform-specific cache and policy paths. + /// The host's home directory, used to resolve cache and managed-file locations. + /// Whether to retain the resolved policy as this runtime process's startup policy, so later work in the process enforces it. Set false to resolve without side effects, which is what a protocol host wants before session creation: its sessions apply the complete policy through the normal lifecycle, and retaining here would let a readiness credential affect unrelated sessions. The returned verdict is identical either way — this decides only whether the process keeps it. + /// The policy-helper invocation reason. Defaults to sessionStart. + /// Whether to run ingestion. Omit or set false to return an empty result without discovering or fetching policy. + /// The authenticated identity to resolve policy for. Omitted before sign-in, in which case no server layer is fetched and the sign-in pin is evaluated against no credential. + /// The session whose credential store, working directory and client kind the resolution should use. Omit it when no session exists yet and the process-wide store applies. A resolution whose session names a credential other than `authInfo` is returned but not retained, so a stale sign-in cannot apply policy for an account that has already been switched away from. + /// A device layer supplied by the host instead of discovered from the machine. Pair it with `hasDeviceResponse` so an explicit absence is distinguishable from an omitted override. + /// Whether `deviceResponse` is an override at all. True with a null `deviceResponse` asserts the device layer is empty; false discovers it from the machine. + /// Whether to ignore a cached server policy when reading, forcing the resolution to come from a live fetch. + /// How this resolution may use the on-disk server-policy cache. Defaults to the runtime's own policy when omitted. Rejected when combined with `bypassCacheRead`, because the two express the same decision and accepting both would leave the caller unsure which one the runtime honored. + /// Whether a configured platform policy helper may be run to produce an enforced helper layer. + /// Directory the policy helper runs in when this resolution is not owned by a registered session. A host resolving policy before its session exists must pass this, or a directory-sensitive helper judges the wrong repository. + /// Name of the embedding client, reported to the managed-settings service as `client_id` so it can refuse to deliver a policy this client is too old to enforce. + /// The to monitor for cancellation requests. The default is . + /// The startup policy a resolution composed, the per-layer outcome flags a host needs to decide how much to trust it, and the enterprise sign-in verdict for the credential it ran against. The flags are reported rather than folded into the settings because a host must distinguish an unmanaged device from a managed device whose policy could not be confirmed, and apply its fail-closed gates in the second case. + internal async Task ResolveStartupPolicyAsync(string userAgent, object? environment, string platform, string homeDirectory, bool retainAndApply, ManagedSettingsPolicyHelperReason? policyHelperReason = null, bool? selfFetch = null, object? authInfo = null, string? sessionId = null, object? deviceResponse = null, bool? hasDeviceResponse = null, bool? bypassCacheRead = null, ManagedSettingsServerPolicyCacheMode? serverPolicyCacheMode = null, bool? policyHelperEnabled = null, string? policyHelperWorkingDirectory = null, string? clientName = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(userAgent); + ArgumentNullException.ThrowIfNull(platform); + ArgumentNullException.ThrowIfNull(homeDirectory); - /// SessionFs APIs. - public ServerSessionFsApi SessionFs => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; + var request = new ManagedSettingsResolveStartupPolicyParams { UserAgent = userAgent, Environment = CopilotClient.ToJsonElementForWire(environment) ?? JsonElement.Parse("null"), Platform = platform, HomeDirectory = homeDirectory, RetainAndApply = retainAndApply, PolicyHelperReason = policyHelperReason, SelfFetch = selfFetch, AuthInfo = CopilotClient.ToJsonElementForWire(authInfo), SessionId = sessionId, DeviceResponse = CopilotClient.ToJsonElementForWire(deviceResponse), HasDeviceResponse = hasDeviceResponse, BypassCacheRead = bypassCacheRead, ServerPolicyCacheMode = serverPolicyCacheMode, PolicyHelperEnabled = policyHelperEnabled, PolicyHelperWorkingDirectory = policyHelperWorkingDirectory, ClientName = clientName }; + return await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.resolveStartupPolicy", [request], cancellationToken); + } - /// LlmInference APIs. - public ServerLlmInferenceApi LlmInference => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; + /// Resolves the managed `remoteControl` policy for an account on its own, for a host deciding whether a remote-control session may start. Composes the same layers as `managedSettings.ingest` and returns only that one key, so a host does not have to know which layer won. An absent policy means the account is unmanaged for this capability and the host may proceed; that default is safe only while absence really does mean "no policy", so a service refusal that says an account policy exists which this client is too old to enforce resolves to an explicit disabled policy instead of to absence. A resolution that cannot be completed is rejected rather than answered as absence, for the same reason. + /// User agent to send with the policy fetch. + /// The host's environment variables, read for cache location and policy overrides. + /// The host's platform, in Node's `process.platform` spelling, used to locate platform-specific cache and policy paths. + /// The host's home directory, used to resolve cache and managed-file locations. + /// The policy-helper invocation reason. Defaults to sessionStart. + /// Whether to run ingestion. Omit or set false to return an empty result without discovering or fetching policy. + /// The authenticated identity to resolve policy for. Omitted before sign-in, in which case no server layer is fetched. + /// A device layer supplied by the host instead of discovered from the machine. Pair it with `hasDeviceResponse` so an explicit absence is distinguishable from an omitted override. + /// Whether `deviceResponse` is an override at all. True with a null `deviceResponse` asserts the device layer is empty; false discovers it from the machine. + /// Whether a configured platform policy helper may be run to produce an enforced helper layer. + /// Name of the embedding client, reported to the managed-settings service as `client_id` so it can refuse to deliver a policy this client is too old to enforce. That refusal is exactly what turns into an explicit `disabled` below, so a host that omits this field forfeits the protection. + /// The to monitor for cancellation requests. The default is . + /// The managed remote-control policy in force for an account, if any. An absent policy means the account is unmanaged for this capability, which a host reads as allowed — so absence here is a positive statement that no policy applies, never a stand-in for a resolution that failed. A resolution that cannot be completed is rejected instead. + internal async Task LoadRemoteControlAsync(string userAgent, object? environment, string platform, string homeDirectory, ManagedSettingsPolicyHelperReason? policyHelperReason = null, bool? selfFetch = null, object? authInfo = null, object? deviceResponse = null, bool? hasDeviceResponse = null, bool? policyHelperEnabled = null, string? clientName = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(userAgent); + ArgumentNullException.ThrowIfNull(platform); + ArgumentNullException.ThrowIfNull(homeDirectory); + + var request = new ManagedSettingsLoadRemoteControlParams { UserAgent = userAgent, Environment = CopilotClient.ToJsonElementForWire(environment) ?? JsonElement.Parse("null"), Platform = platform, HomeDirectory = homeDirectory, PolicyHelperReason = policyHelperReason, SelfFetch = selfFetch, AuthInfo = CopilotClient.ToJsonElementForWire(authInfo), DeviceResponse = CopilotClient.ToJsonElementForWire(deviceResponse), HasDeviceResponse = hasDeviceResponse, PolicyHelperEnabled = policyHelperEnabled, ClientName = clientName }; + return await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.loadRemoteControl", [request], cancellationToken); + } + + /// Evaluates the device-channel enterprise sign-in pin (MDM plist, registry, or managed-settings file) against a credential, without resolving any server-delivered policy. This is the gate an entry point runs before it branches, and the only one that can refuse a developer's first sign-in: it is local, needs no credential, and a server-delivered pin only ever reaches accounts already inside the organization. It fails closed by construction — a discovery that cannot determine whether a pin exists refuses rather than allows, because a control that fails open under a triggerable condition is not a control. Hosts that also want the server channel should call `managedSettings.resolveStartupPolicy`, which folds both channels into one verdict. + /// The host's environment variables. Read for the variables that would make an identity's membership snapshot forgeable, so a credential whose provenance cannot be trusted is refused rather than accepted. + /// The authenticated identity to judge. Omitted when signed out, which is the case the device channel exists to serve: it can refuse a first sign-in before any credential exists. + /// The to monitor for cancellation requests. The default is . + /// The verdict of the `forceLoginOrgs` enterprise sign-in pin for one credential. A managed device may pin Copilot to an approved set of GitHub organizations so a developer cannot sign in with a personal — or another enterprise's — account and escape every control that keys off the signed-in identity. A host must refuse to proceed when `allowed` is false; the remaining fields explain the refusal rather than qualifying it. + internal async Task ResolveDeviceForceLoginOrgsGateAsync(object? environment, object? authInfo = null, CancellationToken cancellationToken = default) + { + var request = new ManagedSettingsResolveDeviceForceLoginOrgsGateParams { Environment = CopilotClient.ToJsonElementForWire(environment) ?? JsonElement.Parse("null"), AuthInfo = CopilotClient.ToJsonElementForWire(authInfo) }; + return await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.resolveDeviceForceLoginOrgsGate", [request], cancellationToken); + } - /// Sessions APIs. - public ServerSessionsApi Sessions => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; + /// Discovers device-managed settings from production MDM and managed-file sources, validates them against the runtime-owned managed-settings schema, and returns the canonical JSON without requiring a session. `managedSettings.resolve` returns the same device settings together with the account's server policy. + /// The to monitor for cancellation requests. The default is . + /// Validated device-managed settings discovered before a session exists. + public async Task ReadAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.read", [], cancellationToken); + } - /// AgentRegistry APIs. - public ServerAgentRegistryApi AgentRegistry => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; + /// Force-refreshes enterprise managed settings for every account: wipes the persistent server-policy cache (the whole `<cacheHome>/managed-settings` directory) and drops this runtime process's in-memory retained server policy. It does not itself fetch policy — the effect is that the next time a session resolves managed settings for an account, that resolution re-fetches the account's org policy from the network instead of serving a cached response. Note that `managedSettings.read` returns only device/MDM settings and never triggers the account server-policy fetch, so a host implementing "sync account policy" should call `managedSettings.resolve` or start a fresh session resolution rather than treat a subsequent `managedSettings.read` as the refreshed org policy. Mirrors the invalidation a sign-out performs, broadened from the one signing-out account to all of them; device/MDM layers describe the machine, not the account, and are left untouched. Rejects if the on-disk cache cannot be removed. + /// The to monitor for cancellation requests. The default is . + public async Task ClearCacheAsync(CancellationToken cancellationToken = default) + { + await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.clearCache", [], cancellationToken); + } - /// Connectors APIs. - public ServerConnectorsApi Connectors => + /// Resolves the effective enterprise managed settings without a session, from the device channel and, when an account is available, the account's server policy through the same per-account cache sessions use. A cached server policy less than an hour old is used without a fetch; otherwise the policy is fetched, and when the fetch fails a cached policy up to 24 hours old is used instead, unless `forceRemoteSettingsRefresh` requires a live fetch. With no account requested or signed in, it reports device policy only; signing out removes the account's cached policy. It can fetch server policy over the network when the cache is stale, so call it off latency-critical paths such as startup rather than before listing models. The policy helper is not run. `layers` lists each channel's document before merging, and `values` and `meta` carry typed effective values and their lock state for the keys typed so far. + /// Opaque account identifier returned by `account.getAllUsers`. When omitted, the current account is used, or device policy only when no account is signed in. + /// GitHub token to resolve instead of the current account. The call fails when the token cannot be resolved. + /// Embedding client identity for server policy requests, as in session creation. Omit for the CLI identity. + /// Working directory used to run an organization policy helper. When omitted, sessionless resolution does not run the helper. + /// The to monitor for cancellation requests. The default is . + /// Effective enterprise managed settings for an account, resolved without a session. + public async Task ResolveAsync(string? selectionId = null, string? gitHubToken = null, string? clientName = null, string? workingDirectory = null, CancellationToken cancellationToken = default) + { + var request = new ManagedSettingsResolveRequest { SelectionId = selectionId, GitHubToken = gitHubToken, ClientName = clientName, WorkingDirectory = workingDirectory }; + return await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.resolve", [request], cancellationToken); + } + + /// Compatibility overload preserving the positional CancellationToken parameter from before workingDirectory was added. + public Task ResolveAsync(string? selectionId, string? gitHubToken, string? clientName, CancellationToken cancellationToken) + => ResolveAsync(selectionId, gitHubToken, clientName, workingDirectory: null, cancellationToken); + + /// Returns the managed-settings authoring JSON schema with descriptive `x-composition` annotations aligned with the shared settings-engine vocabulary. These annotations are not a complete runtime composition contract: model, effortLevel, and contextTier remain coupled. Use `managedSettings.compose` for the runtime's effective result. Performs no I/O. + /// The to monitor for cancellation requests. The default is . + /// The authoring JSON schema for managed settings recognized by this runtime. + public async Task SchemaAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.schema", [], cancellationToken); + } + + /// Validates a candidate managed-settings document the way the runtime validates delivered policy, without applying it. Reports errors that would reject the document, warnings for content the runtime ignores, and the canonical document it would apply. Document text nested more than 64 levels deep is rejected. Performs no I/O. + /// The document to validate: a JSON object, or a string containing the document's JSON text. Preview documents are limited to 1 MiB and 64 levels of nesting, a stricter resource limit than delivered-policy parsing; violations are returned as diagnostics. + /// Channel the document is meant for (`device`, `server`, or `policyHelper`). Some keys are only honored in some channels; for example, a `policyHelper` registration is ignored in policy-helper output. When omitted, no channel-specific checks run. + /// The to monitor for cancellation requests. The default is . + /// Result of validating a managed-settings document. + public async Task ValidateAsync(object? content, string? layer = null, CancellationToken cancellationToken = default) + { + var request = new ManagedSettingsValidateRequest { Content = CopilotClient.ToJsonElementForWire(content) ?? JsonElement.Parse("null"), Layer = layer }; + return await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.validate", [request], cancellationToken); + } + + /// Merges candidate managed-settings documents for the device, server, and policy-helper channels into the effective settings the runtime would enforce on this host, using the same precedence and composition rules as live resolution, without applying them. Like live resolution, a server's advisory sandbox force-enable is declined on a host that cannot run the sandbox. Does not fetch policy or read policy files, but may perform blocking OS or subprocess probes for sandbox support. Preview documents are limited to 1 MiB and 64 levels of nesting. + /// One entry per channel. `source` must be `device`, `server`, or `policyHelper`, each at most once (checked at runtime); order does not matter, because channel precedence is fixed. To preview documents from resolve output, map recognized source strings to ManagedSettingsChannel and copy their settings; generated resolve and compose layer types are distinct. Omitted settings means this channel delivered no document. Supplied documents must be valid within the preview limits; warnings are returned in diagnostics. Compose does not reproduce source-failure state or retained enforcement floors from resolve. + /// The to monitor for cancellation requests. The default is . + /// The effective managed settings the runtime would enforce for the given documents, in the same shape `managedSettings.resolve` returns. + public async Task ComposeAsync(IList layers, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(layers); + + var request = new ManagedSettingsComposeRequest { Layers = layers }; + return await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.compose", [request], cancellationToken); + } + + /// Permissions APIs. + public ServerManagedSettingsPermissionsApi Permissions => field ?? Interlocked.CompareExchange(ref field, new(_rpc), null) ?? field; } +/// Provides server-scoped ManagedSettingsPermissions APIs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ServerManagedSettingsPermissionsApi +{ + private readonly JsonRpc _rpc; + + internal ServerManagedSettingsPermissionsApi(JsonRpc rpc) + { + _rpc = rpc; + } + + /// Pure, sessionless evaluation of operations against a supplied permissionsContext. Performs no policy discovery, I/O, prompting, navigation, permission grants, or sandbox changes. Obtain the context from managedSettings.resolve or session.managedSettings.get, or reuse an in-memory context. Results preserve input order and duplicates. Consumers must block deny, obtain approval for ask, and use their normal permission flow for unmanaged. Unknown policy returns failClosed with every operation denied. This is not a substitute for user permissions or other security controls. + /// Trusted, reusable managed permission policy snapshot; no policy is discovered during evaluation. + /// Operations to evaluate, preserving input order and duplicates. An empty batch is valid. + /// The to monitor for cancellation requests. The default is . + /// Ordered managed permission verdicts and the supplied policy's fail-closed posture. + public async Task EvaluateAsync(ManagedPermissionsContext context, IList operations, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(context); + ArgumentNullException.ThrowIfNull(operations); + + var request = new ManagedSettingsPermissionsEvaluateRequest { Context = context, Operations = operations }; + return await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.permissions.evaluate", [request], cancellationToken); + } +} + /// Provides server-scoped Environments APIs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed class ServerEnvironmentsApi @@ -43713,6 +48205,16 @@ public async Task DiscoverAsync(IList? projectPaths var request = new HooksDiscoverRequest { ProjectPaths = projectPaths, ExcludeHostHooks = excludeHostHooks }; return await CopilotClient.InvokeRpcAsync(_rpc, "hooks.discover", [request], cancellationToken); } + + /// Discovers hook actions enabled under server-side discovery settings from user, repository, plugin, and managed-policy sources. + /// Optional project paths and host-exclusion behavior for server-scoped hook discovery. + /// The to monitor for cancellation requests. The default is . + /// Server-discovered hook actions and partial-load diagnostics from user, repository, plugin, and managed-policy sources. Concrete sessions may include additional session-specific hook sources. + public async Task DiscoverAsync(HooksDiscoverRequest request, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(request); + return await CopilotClient.InvokeRpcAsync(_rpc, "hooks.discover", [request], cancellationToken); + } } /// Provides server-scoped Models APIs. @@ -43746,92 +48248,6 @@ public async Task GetBuiltInCatalogAsync(CancellationToken } } -/// Provides server-scoped Sandbox APIs. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ServerSandboxApi -{ - private readonly JsonRpc _rpc; - - internal ServerSandboxApi(JsonRpc rpc) - { - _rpc = rpc; - } - - /// Reports whether the host running this runtime can run the command sandbox, without starting a session or spawning a sandboxed command. - /// The to monitor for cancellation requests. The default is . - /// Whether the host running this runtime can run the command sandbox. The runtime checks `supported` once per process. A capability answer can change while the process runs, for example after the user installs a missing package. - public async Task GetHostSupportAsync(CancellationToken cancellationToken = default) - { - return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.getHostSupport", [], cancellationToken); - } - - /// ProxyCa APIs. - public ServerSandboxProxyCaApi ProxyCa => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; -} - -/// Provides server-scoped SandboxProxyCa APIs. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ServerSandboxProxyCaApi -{ - private readonly JsonRpc _rpc; - - internal ServerSandboxProxyCaApi(JsonRpc rpc) - { - _rpc = rpc; - } - - /// Reports whether the persistent certificate authority of the sandbox credential proxy exists, whether OS trust includes it, and whether it must be rotated. Changes nothing. - /// The sandbox configuration that the host gives its sessions. The runtime reads the credential hosts from `auth` and `credentials`; it ignores `enabled` and the other fields. - /// The to monitor for cancellation requests. The default is . - /// Status of the persistent certificate authority of the sandbox credential proxy. - public async Task GetStatusAsync(SandboxConfig? sandboxConfig = null, CancellationToken cancellationToken = default) - { - var request = new SandboxProxyCaRequest { SandboxConfig = sandboxConfig }; - return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.proxyCa.getStatus", [request], cancellationToken); - } - - /// Creates the persistent certificate authority of the sandbox credential proxy if none is stored, without changing OS trust, and returns the path of its public certificate. Keeps an existing certificate authority, even one that must be rotated. Fails where OS trust is unsupported. Trust it with sandbox.proxyCa.trust: the CLI trusts only the hosts in the saved user settings, so it refuses a certificate authority that also covers hosts from sandboxConfig. - /// The sandbox configuration that the host gives its sessions. The runtime reads the credential hosts from `auth` and `credentials`; it ignores `enabled` and the other fields. - /// The to monitor for cancellation requests. The default is . - /// Result of creating the persistent certificate authority of the sandbox credential proxy. - public async Task CreateAsync(SandboxConfig? sandboxConfig = null, CancellationToken cancellationToken = default) - { - var request = new SandboxProxyCaRequest { SandboxConfig = sandboxConfig }; - return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.proxyCa.create", [request], cancellationToken); - } - - /// Replaces the persistent certificate authority of the sandbox credential proxy with a new one for the current credential hosts. If OS trust included the old one, removes it and trusts the new one, which can show an OS authentication prompt. Running sandboxed tools keep the old certificate authority until they restart. - /// The sandbox configuration that the host gives its sessions. The runtime reads the credential hosts from `auth` and `credentials`; it ignores `enabled` and the other fields. - /// The to monitor for cancellation requests. The default is . - /// Status of the persistent certificate authority of the sandbox credential proxy. - public async Task RotateAsync(SandboxConfig? sandboxConfig = null, CancellationToken cancellationToken = default) - { - var request = new SandboxProxyCaRequest { SandboxConfig = sandboxConfig }; - return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.proxyCa.rotate", [request], cancellationToken); - } - - /// Adds the persistent certificate authority of the sandbox credential proxy to OS trust, so sandboxed clients that read only OS trust accept the proxy. Call create first. Refuses a certificate authority that is not constrained to the current credential hosts. Can show an OS authentication prompt. - /// The sandbox configuration that the host gives its sessions. The runtime reads the credential hosts from `auth` and `credentials`; it ignores `enabled` and the other fields. - /// The to monitor for cancellation requests. The default is . - /// Status of the persistent certificate authority of the sandbox credential proxy. - public async Task TrustAsync(SandboxConfig? sandboxConfig = null, CancellationToken cancellationToken = default) - { - var request = new SandboxProxyCaRequest { SandboxConfig = sandboxConfig }; - return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.proxyCa.trust", [request], cancellationToken); - } - - /// Removes the persistent certificate authority of the sandbox credential proxy from OS trust. Keeps the stored certificate authority. Can show an OS authentication prompt. Sandboxed clients that read only OS trust then reject the proxy; clients that read the per-process certificate bundle continue to work. - /// The to monitor for cancellation requests. The default is . - /// Status of the persistent certificate authority of the sandbox credential proxy. - public async Task RemoveAsync(CancellationToken cancellationToken = default) - { - return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.proxyCa.remove", [], cancellationToken); - } -} - /// Provides server-scoped Tools APIs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed class ServerToolsApi @@ -43917,6 +48333,51 @@ public async Task LogoutAsync(string? selectionId = null, A var request = new AccountLogoutRequest { SelectionId = selectionId, AuthInfo = authInfo }; return await CopilotClient.InvokeRpcAsync(_rpc, "account.logout", [request], cancellationToken); } + + /// Renders the sign-in banner and the one-line account description the runtime uses for an authentication identity. The runtime words these in several places for its own callers, so a host that announces a sign-in or an account switch should render them from here rather than composing its own, which would let the two spellings drift apart. + /// Identity to describe. + /// The to monitor for cancellation requests. The default is . + /// The wording the runtime uses for an authentication identity. + internal async Task DescribeAuthInfoAsync(AuthIdentity identity, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(identity); + + var request = new AccountDescribeAuthInfoRequest { Identity = identity }; + return await CopilotClient.InvokeRpcAsync(_rpc, "account.describeAuthInfo", [request], cancellationToken); + } + + /// Reports whether the supplied identity can create gists. Gists are unavailable on GitHub Enterprise Cloud with data residency, and to Enterprise Managed Users on github.com, so a host that offers gist actions should hide or disable them when this reports no support. + /// Identity to evaluate. + /// The to monitor for cancellation requests. The default is . + /// Whether the identity can create gists, and why not when it cannot. + internal async Task GetGistSupportAsync(AuthIdentity identity, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(identity); + + var request = new AccountGetGistSupportRequest { Identity = identity }; + return await CopilotClient.InvokeRpcAsync(_rpc, "account.getGistSupport", [request], cancellationToken); + } + + /// Resolves a path on the identity's GitHub host into an absolute URL, so a host can link to settings or documentation on the enterprise instance the user is signed in to rather than to github.com. Falls back to the public host when no identity is supplied. + /// Host-relative path, without a leading slash, for example `settings/copilot/features`. + /// Identity whose host the path resolves against. Omit to resolve against the public github.com host. + /// The to monitor for cancellation requests. The default is . + /// An absolute URL on the identity's GitHub host. + internal async Task GetWebUrlAsync(string path, AuthIdentity? identity = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(path); + + var request = new AccountGetWebUrlRequest { Path = path, Identity = identity }; + return await CopilotClient.InvokeRpcAsync(_rpc, "account.getWebUrl", [request], cancellationToken); + } + + /// Names the environment variable supplying a classic personal access token, if one is set. A classic token cannot be upgraded in place, so a host that finds one should tell the user which variable to unset rather than starting an interactive sign-in that the variable would override. + /// The to monitor for cancellation requests. The default is . + /// The environment variable supplying a classic personal access token, when one is set. + internal async Task GetTokenEnvVarAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "account.getTokenEnvVar", [], cancellationToken); + } } /// Provides server-scoped Secrets APIs. @@ -43965,6 +48426,26 @@ public async Task DiscoverAsync(string? workingDirectory = nu return await CopilotClient.InvokeRpcAsync(_rpc, "mcp.discover", [request], cancellationToken); } + /// Lists the MCP servers the runtime supplies itself, so a host that disables the built-ins wholesale suppresses exactly the set the runtime would otherwise add. The names are fixed for a build and depend on no configuration, session or account. + /// The to monitor for cancellation requests. The default is . + /// Names of the MCP servers the runtime supplies itself. + internal async Task ListBuiltinServerNamesAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "mcp.listBuiltinServerNames", [], cancellationToken); + } + + /// Reports whether the gh-replaceable GitHub MCP tools may be clipped for a session, which a host feeds back as the `excludeGhReplaceableTools` build option. The tools are redundant only when the session can reach a shell *and* the host has the `gh` that would replace them, so both halves are decided here. The shell half runs the platform shell tool through the runtime's own tool-filter matcher, so entry forms like `builtin:bash` and `builtin:*` behave exactly as they do when a session builds its tool catalog. The host half probes for `gh`, and is skipped entirely when the filters already rule the shell out. Host presence alone is not enough: a session restricted to, say, `view` would otherwise lose the built-in issue-read fallback while having no shell to replace it with. The answer describes the host the runtime runs on, so it is never forwarded to a remote engine. + /// The session's tool allowlist, when it set one. Omitted means the session constrains nothing this way. + /// The session's tool denylist, when it set one. Omitted means the session constrains nothing this way. + /// How the allowlist and denylist combine when both are set. Omitted means the default every session gets, so a caller that never chose a precedence is answered as its sessions behave. + /// The to monitor for cancellation requests. The default is . + /// Whether the gh-replaceable GitHub MCP tools may be clipped for the session described by the request. + internal async Task ShouldExcludeGitHubToolsAsync(IList? availableTools = null, IList? excludedTools = null, OptionsUpdateToolFilterPrecedence? toolFilterPrecedence = null, CancellationToken cancellationToken = default) + { + var request = new McpShouldExcludeGitHubToolsRequest { AvailableTools = availableTools, ExcludedTools = excludedTools, ToolFilterPrecedence = toolFilterPrecedence }; + return await CopilotClient.InvokeRpcAsync(_rpc, "mcp.shouldExcludeGitHubTools", [request], cancellationToken); + } + /// Requests a side-effect-free MCP install plan from a catalog candidate handle or a caller-supplied card. This host-implemented server method is available through SDK/TUI hosts; standalone and C-ABI runtimes whose host does not implement server-method dispatch return JSON-RPC MethodNotFound. A runtime with planning available returns a normalised plan and opaque single-use plan handle; a runtime without it returns the typed planning-unavailable result. A completed plan reports resource identity, provenance, eligible transport choices, the user-scope target, required typed values and secret placeholders, the policy result, the configuration changes installing would make, and whether a reload would be needed. Planning never writes configuration, stores a secret, or reloads MCP servers, so abandoning a plan needs no call and leaves nothing behind. /// Protocol version and capabilities the caller requires. /// What to plan: either a candidate handle from a previous search, or a card supplied directly. @@ -44077,6 +48558,12 @@ public async Task ApplyUninstallAsync(CatalogClientContra Interlocked.CompareExchange(ref field, new(_rpc), null) ?? field; + /// Registry APIs. + public ServerMcpRegistryApi Registry => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + /// Installations APIs. public ServerMcpInstallationsApi Installations => field ?? @@ -44107,12 +48594,11 @@ public async Task ListAsync(CancellationToken cancellationToken = /// Unique name for the MCP server. /// MCP server configuration (stdio process or remote HTTP/SSE). /// The to monitor for cancellation requests. The default is . - public async Task AddAsync(string name, object config, CancellationToken cancellationToken = default) + public async Task AddAsync(string name, object? config, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(name); - ArgumentNullException.ThrowIfNull(config); - var request = new McpConfigAddRequest { Name = name, Config = CopilotClient.ToJsonElementForWire(config)!.Value }; + var request = new McpConfigAddRequest { Name = name, Config = CopilotClient.ToJsonElementForWire(config) ?? JsonElement.Parse("null") }; await CopilotClient.InvokeRpcAsync(_rpc, "mcp.config.add", [request], cancellationToken); } @@ -44120,12 +48606,11 @@ public async Task AddAsync(string name, object config, CancellationToken cancell /// Name of the MCP server to update. /// MCP server configuration (stdio process or remote HTTP/SSE). /// The to monitor for cancellation requests. The default is . - public async Task UpdateAsync(string name, object config, CancellationToken cancellationToken = default) + public async Task UpdateAsync(string name, object? config, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(name); - ArgumentNullException.ThrowIfNull(config); - var request = new McpConfigUpdateRequest { Name = name, Config = CopilotClient.ToJsonElementForWire(config)!.Value }; + var request = new McpConfigUpdateRequest { Name = name, Config = CopilotClient.ToJsonElementForWire(config) ?? JsonElement.Parse("null") }; await CopilotClient.InvokeRpcAsync(_rpc, "mcp.config.update", [request], cancellationToken); } @@ -44171,6 +48656,50 @@ public async Task ReloadAsync(CancellationToken cancellationToken = default) } } +/// Provides server-scoped McpRegistry APIs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ServerMcpRegistryApi +{ + private readonly JsonRpc _rpc; + + internal ServerMcpRegistryApi(JsonRpc rpc) + { + _rpc = rpc; + } + + /// Allocates an ID for one cancellable MCP registry search. The ID exists before the search starts, so callers can cancel before it starts. The networking stack supplies the cancellation namespace. The runtime retains at most 1,024 unused IDs. At capacity, another allocation can reclaim an unused ID. Active searches retain their IDs until they finish. + /// The to monitor for cancellation requests. The default is . + /// Request id naming a cancellable registry search. + internal async Task AllocateRequestIdAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "mcp.registry.allocateRequestId", [], cancellationToken); + } + + /// Searches the MCP registry the supplied credential may read, resolving the registry endpoint from policy first. Hosts usually send credential-free `AuthIdentity`; a `token` identity can be resolved only when it embeds a token, while `env` and `gh-cli` identities can use a token embedded in the request first. The runtime follows registry pages, keeps the newest entry per server name, cuts the list to `limit`, and sorts an empty-query result by GitHub stars. Each server object is carried opaquely. + /// Request ID from `mcp.registry.allocateRequestId`. The search refuses unknown, reclaimed, or canceled IDs and IDs that another search or request already uses. + /// The credential the search runs under, carried opaquely. Hosts usually send credential-free `AuthIdentity`; a `token` identity can be resolved only when it embeds a token, while `env` and `gh-cli` identities can use a token embedded in the request first. + /// Maximum number of servers to return. + /// Free-text query. Omitted or empty asks the registry for its top servers rather than searching. A value that is not a string is refused. + /// Repository used for the policy lookup, as `owner/name`. The policy selects the registry URL and whether the user token goes to the registry. The registry receives this repository only when the policy entry lists it as required context. A value that is not a string is refused. + /// The to monitor for cancellation requests. The default is . + /// Servers selected from the registry response. + internal async Task SearchAsync(long requestId, object? authInfo, long limit, string? query = null, string? repository = null, CancellationToken cancellationToken = default) + { + var request = new McpRegistrySearchRequest { RequestId = requestId, AuthInfo = CopilotClient.ToJsonElementForWire(authInfo) ?? JsonElement.Parse("null"), Limit = limit, Query = query, Repository = repository }; + return await CopilotClient.InvokeRpcAsync(_rpc, "mcp.registry.search", [request], cancellationToken); + } + + /// Abandons the registry search that uses the given request ID. It acts only on IDs from `mcp.registry.allocateRequestId`, so it never cancels another component's request. Answers `canceled: true` when it stops a running search. Answers `canceled: false` for unknown or reclaimed IDs, completed or canceled searches, and unused reservations. It releases an unused reservation, so a later search with that ID is refused. + /// Request ID from `mcp.registry.allocateRequestId` that the search uses. + /// The to monitor for cancellation requests. The default is . + /// Whether the cancel reached a live search. + internal async Task CancelAsync(long requestId, CancellationToken cancellationToken = default) + { + var request = new McpRegistryCancelRequest { RequestId = requestId }; + return await CopilotClient.InvokeRpcAsync(_rpc, "mcp.registry.cancel", [request], cancellationToken); + } +} + /// Provides server-scoped McpInstallations APIs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed class ServerMcpInstallationsApi @@ -44281,6 +48810,92 @@ public async Task DisableAsync(IList ids, CancellationToken cancellation } } +/// Provides server-scoped Instructions APIs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ServerInstructionsApi +{ + private readonly JsonRpc _rpc; + + internal ServerInstructionsApi(JsonRpc rpc) + { + _rpc = rpc; + } + + /// Returns the prompt the `init` command runs to author repository instructions. A host offering its own initialize action sends this text so the instructions it produces match the ones the CLI produces. The text is fixed for a build and depends on no configuration, session or repository. + /// The to monitor for cancellation requests. The default is . + /// The prompt the init command runs. + internal async Task GetInitPromptAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "instructions.getInitPrompt", [], cancellationToken); + } + + /// Discovers instruction sources across user, repository, and plugin sources. + /// Optional list of project directory paths to scan for repository/working-directory instruction sources. When omitted or empty, only user-level and plugin instruction sources are returned (no project scan). + /// When true, omit the host's instruction sources (user/home-level files and plugin rules), leaving only repository and working-directory sources. For multitenant deployments. + /// The to monitor for cancellation requests. The default is . + /// Instruction sources discovered across user, repository, and plugin sources. + public async Task DiscoverAsync(IList? projectPaths = null, bool? excludeHostInstructions = null, CancellationToken cancellationToken = default) + { + var request = new InstructionsDiscoverRequest { ProjectPaths = projectPaths, ExcludeHostInstructions = excludeHostInstructions }; + return await CopilotClient.InvokeRpcAsync(_rpc, "instructions.discover", [request], cancellationToken); + } + + /// Discovers instruction sources across user, repository, and plugin sources. + /// Optional project paths to include in instruction discovery. + /// The to monitor for cancellation requests. The default is . + /// Instruction sources discovered across user, repository, and plugin sources. + public async Task DiscoverAsync(InstructionsDiscoverRequest request, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(request); + return await CopilotClient.InvokeRpcAsync(_rpc, "instructions.discover", [request], cancellationToken); + } + + /// Discovers the instruction sources for one caller-named location and returns them in the order the runtime applies them. `discover` answers for a set of project paths and derives the rest of its scope itself; this one instead takes every argument from the caller — the extra directories to scan and whether to descend into nested instruction files — and honors them as given. It also includes instruction directories from trusted repository settings, matching native repository discovery. Use it when the caller knows its own scope and no session exists to derive that scope from, such as a host reporting how many instruction files a directory contributes before any session starts. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract; it is migration plumbing, not a surface consumers are meant to depend on. + /// Repository location to discover. An empty string selects no repository scope; cwdPath can still supply working-directory sources. + /// When true, include the sources a command-line host reads. Hosts that are not the CLI omit it. + /// Working directory whose instruction sources should also be included. Omission adds no separate working-directory scope. + /// Copilot configuration directory for user-level sources. An omitted or empty value uses the server's configuration home. + /// Extra directories to scan alongside instruction directories configured in the runtime environment. + /// When true, also discover instruction files nested below the location. + /// The to monitor for cancellation requests. The default is . + /// Instruction sources discovered across user, repository, and plugin sources. + internal async Task DiscoverRepoSourcesAsync(string location, bool? isCli = null, string? cwdPath = null, string? configDir = null, IList? additionalDirs = null, bool? enableChildInstructions = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(location); + + var request = new InstructionsDiscoverRepoSourcesRequest { Location = location, IsCli = isCli, CwdPath = cwdPath, ConfigDir = configDir, AdditionalDirs = additionalDirs, EnableChildInstructions = enableChildInstructions }; + return await CopilotClient.InvokeRpcAsync(_rpc, "instructions.discoverRepoSources", [request], cancellationToken); + } + + /// 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. + /// Optional list of project directory paths. When omitted or empty, only the user-level targets are returned. + /// When true, omit the host's user-level instruction targets, leaving only repository targets. For multitenant deployments (mirrors `discover`'s `excludeHostInstructions`). + /// The to monitor for cancellation requests. The default is . + /// Canonical files and directories where custom instructions can be created so the runtime will recognize them. + public async Task GetDiscoveryPathsAsync(IList? projectPaths = null, bool? excludeHostInstructions = null, CancellationToken cancellationToken = default) + { + var request = new InstructionsGetDiscoveryPathsRequest { ProjectPaths = projectPaths, ExcludeHostInstructions = excludeHostInstructions }; + return await CopilotClient.InvokeRpcAsync(_rpc, "instructions.getDiscoveryPaths", [request], cancellationToken); + } + + /// 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. + /// Optional project paths to include when enumerating instruction discovery targets. + /// The to monitor for cancellation requests. The default is . + /// Canonical files and directories where custom instructions can be created so the runtime will recognize them. + public async Task GetDiscoveryPathsAsync(InstructionsGetDiscoveryPathsRequest request, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(request); + return await CopilotClient.InvokeRpcAsync(_rpc, "instructions.getDiscoveryPaths", [request], cancellationToken); + } + + /// Drops the runtime's in-memory cache of discovered custom instructions so the next discovery reads them from disk again. A host calls this after it changes instruction files, or after it trusts a folder whose instructions an earlier discovery skipped, so that a session started afterwards sees the current files instead of a cached earlier answer. The call performs no I/O of its own and removes nothing from disk; it only invalidates, so the cost lands on the next discovery. Returns once the cache is clear, which is what lets a caller order a session start after it. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract; it is migration plumbing, not a surface consumers are meant to depend on. + /// The to monitor for cancellation requests. The default is . + internal async Task ClearCacheAsync(CancellationToken cancellationToken = default) + { + await CopilotClient.InvokeRpcAsync(_rpc, "instructions.clearCache", [], cancellationToken); + } +} + /// Provides server-scoped Skills APIs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed class ServerSkillsApi @@ -44358,7 +48973,7 @@ public async Task ApplyUninstallAsync(CatalogClientCont /// Discovers skills across global and project sources. /// Optional list of project directory paths to scan for project-scoped skills. - /// Optional list of additional skill directory paths to include. + /// Explicit discovery-only skill roots, not filesystem grants. Available without ambient discovery, subject to skill/managed disables. Relative paths resolve against the request's effective working directory when supplied. Null and omission are equivalent; an empty array contributes no explicit roots. /// Optional skill scan paths to exclude from discovery. /// When true, omit skills from the host's global sources (personal, custom, plugin, and built-in), returning only project-scoped skills. For multitenant deployments. /// The to monitor for cancellation requests. The default is . @@ -44381,6 +48996,16 @@ public async Task GetDiscoveryPathsAsync(IList? return await CopilotClient.InvokeRpcAsync(_rpc, "skills.getDiscoveryPaths", [request], cancellationToken); } + /// 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. + /// Optional project paths to enumerate. + /// The to monitor for cancellation requests. The default is . + /// Canonical locations where skills can be created so the runtime will recognize them. + public async Task GetDiscoveryPathsAsync(SkillsGetDiscoveryPathsRequest request, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(request); + return await CopilotClient.InvokeRpcAsync(_rpc, "skills.getDiscoveryPaths", [request], cancellationToken); + } + /// Installations APIs. public ServerSkillsInstallationsApi Installations => field ?? @@ -44786,6 +49411,16 @@ public async Task DiscoverAsync(IList? projectPaths = n return await CopilotClient.InvokeRpcAsync(_rpc, "agents.discover", [request], cancellationToken); } + /// Discovers custom agents across user, project, plugin, and remote sources. + /// Optional project paths to include in agent discovery. + /// The to monitor for cancellation requests. The default is . + /// Agents discovered across user, project, plugin, and remote sources. + public async Task DiscoverAsync(AgentsDiscoverRequest request, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(request); + return await CopilotClient.InvokeRpcAsync(_rpc, "agents.discover", [request], cancellationToken); + } + /// 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. /// Optional list of project directory paths. When omitted or empty, only the user-level directory is returned. /// When true, omit the host's user-level agent directory, leaving only project directories. For multitenant deployments (mirrors `discover`'s `excludeHostAgents`). @@ -44797,6 +49432,16 @@ public async Task GetDiscoveryPathsAsync(IList? return await CopilotClient.InvokeRpcAsync(_rpc, "agents.getDiscoveryPaths", [request], cancellationToken); } + /// 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. + /// Optional project paths to include when enumerating agent discovery directories. + /// The to monitor for cancellation requests. The default is . + /// Canonical locations where custom agents can be created so the runtime will recognize them. + public async Task GetDiscoveryPathsAsync(AgentsGetDiscoveryPathsRequest request, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(request); + return await CopilotClient.InvokeRpcAsync(_rpc, "agents.getDiscoveryPaths", [request], cancellationToken); + } + /// Lists the agents this runtime ships, by name. A consumer separating shipped agents from ones the user or a plugin authored should compare against these names rather than against `AgentInfo.source`: an authored agent may carry the `builtin` source while not being one of these, and the runtime treats the two as separate questions. `disableableNames` is the subset a user may turn off, which a client needs to decide whether to offer a toggle. `yamlBasedNames` is the subset backed by a shipped YAML definition, which a client needs before asking the runtime to load one. /// The to monitor for cancellation requests. The default is . /// The agents this runtime ships, named so a consumer can tell them apart from authored ones. @@ -44856,40 +49501,6 @@ internal async Task CustomAgentInit } } -/// Provides server-scoped Instructions APIs. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ServerInstructionsApi -{ - private readonly JsonRpc _rpc; - - internal ServerInstructionsApi(JsonRpc rpc) - { - _rpc = rpc; - } - - /// Discovers instruction sources across user, repository, and plugin sources. - /// Optional list of project directory paths to scan for repository/working-directory instruction sources. When omitted or empty, only user-level and plugin instruction sources are returned (no project scan). - /// When true, omit the host's instruction sources (user/home-level files and plugin rules), leaving only repository and working-directory sources. For multitenant deployments. - /// The to monitor for cancellation requests. The default is . - /// Instruction sources discovered across user, repository, and plugin sources. - public async Task DiscoverAsync(IList? projectPaths = null, bool? excludeHostInstructions = null, CancellationToken cancellationToken = default) - { - var request = new InstructionsDiscoverRequest { ProjectPaths = projectPaths, ExcludeHostInstructions = excludeHostInstructions }; - return await CopilotClient.InvokeRpcAsync(_rpc, "instructions.discover", [request], cancellationToken); - } - - /// 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. - /// Optional list of project directory paths. When omitted or empty, only the user-level targets are returned. - /// When true, omit the host's user-level instruction targets, leaving only repository targets. For multitenant deployments (mirrors `discover`'s `excludeHostInstructions`). - /// The to monitor for cancellation requests. The default is . - /// Canonical files and directories where custom instructions can be created so the runtime will recognize them. - public async Task GetDiscoveryPathsAsync(IList? projectPaths = null, bool? excludeHostInstructions = null, CancellationToken cancellationToken = default) - { - var request = new InstructionsGetDiscoveryPathsRequest { ProjectPaths = projectPaths, ExcludeHostInstructions = excludeHostInstructions }; - return await CopilotClient.InvokeRpcAsync(_rpc, "instructions.getDiscoveryPaths", [request], cancellationToken); - } -} - /// Provides server-scoped GlobalState APIs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed class ServerGlobalStateApi @@ -44919,7 +49530,7 @@ internal async Task LoadForConfigDirAsync(string? configD return await CopilotClient.InvokeRpcAsync(_rpc, "globalState.loadForConfigDir", [request], cancellationToken); } - /// Records one top-level key in the host's machine-wide state, the counterpart to `globalState.load`. A host calls this to remember that it has shown an onboarding step, asked a one-off question, or completed a migration, so the next run can skip it. Only the named key is replaced and the rest of the document is preserved, which lets two writers record different flags without overwriting each other; passing no value removes the key instead. Only the keys a host records itself are writable: `appInstallNudgeResponded`, `appTipShown`, `askedSetupTerminals`, `autoFeedbackLastPromptedAt`, `firstLaunchAt`, `recentModelIds`, `sandboxCredentialProxyCaDeclined` and `sandboxOnboardingShown`. Every other key is refused, including `installedPlugins`, the stored credentials, `trustedFolders`, the staff flags and the signed-in accounts. Plugin enablement must use the plugin APIs, which apply repository and managed-policy checks. + /// Records one top-level key in the host's machine-wide state, the counterpart to `globalState.load`. A host calls this to remember that it has shown an onboarding step, asked a one-off question, or completed a migration, so the next run can skip it. Only the named key is replaced and the rest of the document is preserved, which lets two writers record different flags without overwriting each other; passing no value removes the key instead. Only the keys a host records itself are writable: `appTipShown`, `askedSetupTerminals`, `autoFeedbackLastPromptedAt`, `firstLaunchAt`, `recentModelIds`, `sandboxCredentialProxyCaDeclined` and `sandboxOnboardingShown`. Every other key is refused, including `installedPlugins`, the stored credentials, `trustedFolders`, the staff flags and the signed-in accounts. Plugin enablement must use the plugin APIs, which apply repository and managed-policy checks. /// Top-level key to write, named as it appears in the result of `globalState.load`. It must be one of the writable keys that `globalState.writeKey` lists. /// Copilot configuration directory to write the state document in, taking precedence over the server's own `COPILOT_HOME` and default home. Omit it, or pass an empty string, to write the directory the server resolved for itself. Mirrors `globalState.loadForConfigDir`, so a caller can read and write the same directory. /// Value to store for the key. Omit it, or pass null, to remove the key instead. @@ -44953,55 +49564,6 @@ public async Task ListAsync(CancellationToken cancellationToken = d } } -/// Provides server-scoped User APIs. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ServerUserApi -{ - private readonly JsonRpc _rpc; - - internal ServerUserApi(JsonRpc rpc) - { - _rpc = rpc; - } - - /// Settings APIs. - public ServerUserSettingsApi Settings => - field ?? - Interlocked.CompareExchange(ref field, new(_rpc), null) ?? - field; -} - -/// Provides server-scoped UserSettings APIs. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ServerUserSettingsApi -{ - private readonly JsonRpc _rpc; - - internal ServerUserSettingsApi(JsonRpc rpc) - { - _rpc = rpc; - } - - /// Lists every known user setting from settings.json, each with its effective value, its default, and whether it is at the default — so settings the user has never set still appear with their default value. Does not include repository- or enterprise-managed overrides that the runtime layers on top at session time. - /// The to monitor for cancellation requests. The default is . - /// Per-key metadata for every known user setting in settings.json, including settings left at their default. Excludes repository- and enterprise-managed overrides. - public async Task GetAsync(CancellationToken cancellationToken = default) - { - return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.get", [], cancellationToken); - } - - /// Writes one or more user settings to settings.json, replacing each provided top-level key. A key whose value is null is removed. - /// Partial user settings to write, as a free-form object keyed by setting name. - /// The to monitor for cancellation requests. The default is . - public async Task SetAsync(object settings, CancellationToken cancellationToken = default) - { - ArgumentNullException.ThrowIfNull(settings); - - var request = new UserSettingsSetRequest { Settings = CopilotClient.ToJsonElementForWire(settings)!.Value }; - await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.set", [request], cancellationToken); - } -} - /// Provides server-scoped GitHubRepository APIs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed class ServerGitHubRepositoryApi @@ -45050,11 +49612,9 @@ internal async Task NextRequestIdAsync(Cancellation /// The credential the listing runs under, carried opaquely because its shape is the host's own and the runtime only resolves a token and a GitHub host from it. No credential travels: this selects one the runtime already holds. /// The to monitor for cancellation requests. The default is . /// Outcome of an owner listing. Exactly one of `owners` and `message` is present, except that `throwError` reports a failure the caller is expected to raise rather than render. - internal async Task ListAsync(long requestId, object authInfo, CancellationToken cancellationToken = default) + internal async Task ListAsync(long requestId, object? authInfo, CancellationToken cancellationToken = default) { - ArgumentNullException.ThrowIfNull(authInfo); - - var request = new GitHubOwnersListRequest { RequestId = requestId, AuthInfo = CopilotClient.ToJsonElementForWire(authInfo)!.Value }; + var request = new GitHubOwnersListRequest { RequestId = requestId, AuthInfo = CopilotClient.ToJsonElementForWire(authInfo) ?? JsonElement.Parse("null") }; return await CopilotClient.InvokeRpcAsync(_rpc, "gitHubOwners.list", [request], cancellationToken); } @@ -45117,83 +49677,6 @@ internal async Task ReposFromRemotesAsync(string gitR } } -/// Provides server-scoped ManagedSettings APIs. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ServerManagedSettingsApi -{ - private readonly JsonRpc _rpc; - - internal ServerManagedSettingsApi(JsonRpc rpc) - { - _rpc = rpc; - } - - /// Discovers device-managed settings from production MDM and managed-file sources, validates them against the runtime-owned managed-settings schema, and returns the canonical JSON without requiring a session. `managedSettings.resolve` returns the same device settings together with the account's server policy. - /// The to monitor for cancellation requests. The default is . - /// Validated device-managed settings discovered before a session exists. - public async Task ReadAsync(CancellationToken cancellationToken = default) - { - return await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.read", [], cancellationToken); - } - - /// Force-refreshes enterprise managed settings for every account: wipes the persistent server-policy cache (the whole `<cacheHome>/managed-settings` directory) and drops this runtime process's in-memory retained server policy. It does not itself fetch policy — the effect is that the next time a session resolves managed settings for an account, that resolution re-fetches the account's org policy from the network instead of serving a cached response. Note that `managedSettings.read` returns only device/MDM settings and never triggers the account server-policy fetch, so a host implementing "sync account policy" should call `managedSettings.resolve` or start a fresh session resolution rather than treat a subsequent `managedSettings.read` as the refreshed org policy. Mirrors the invalidation a sign-out performs, broadened from the one signing-out account to all of them; device/MDM layers describe the machine, not the account, and are left untouched. Rejects if the on-disk cache cannot be removed. - /// The to monitor for cancellation requests. The default is . - public async Task ClearCacheAsync(CancellationToken cancellationToken = default) - { - await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.clearCache", [], cancellationToken); - } - - /// Resolves the effective enterprise managed settings without a session, from the device channel and, when an account is available, the account's server policy through the same per-account cache sessions use. A cached server policy less than an hour old is used without a fetch; otherwise the policy is fetched, and when the fetch fails a cached policy up to 24 hours old is used instead, unless `forceRemoteSettingsRefresh` requires a live fetch. With no account requested or signed in, it reports device policy only; signing out removes the account's cached policy. It can fetch server policy over the network when the cache is stale, so call it off latency-critical paths such as startup rather than before listing models. The policy helper is not run. `layers` lists each channel's document before merging, and `values` and `meta` carry typed effective values and their lock state for the keys typed so far. - /// Opaque account identifier returned by `account.getAllUsers`. When omitted, the current account is used, or device policy only when no account is signed in. - /// GitHub token to resolve instead of the current account. The call fails when the token cannot be resolved. - /// Embedding client identity for server policy requests, as in session creation. Omit for the CLI identity. - /// Working directory used to run an organization policy helper. When omitted, sessionless resolution does not run the helper. - /// The to monitor for cancellation requests. The default is . - /// Effective enterprise managed settings for an account, resolved without a session. - public async Task ResolveAsync(string? selectionId = null, string? gitHubToken = null, string? clientName = null, string? workingDirectory = null, CancellationToken cancellationToken = default) - { - var request = new ManagedSettingsResolveRequest { SelectionId = selectionId, GitHubToken = gitHubToken, ClientName = clientName, WorkingDirectory = workingDirectory }; - return await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.resolve", [request], cancellationToken); - } - - /// Compatibility overload preserving the positional CancellationToken parameter from before workingDirectory was added. - public Task ResolveAsync(string? selectionId, string? gitHubToken, string? clientName, CancellationToken cancellationToken) - => ResolveAsync(selectionId, gitHubToken, clientName, workingDirectory: null, cancellationToken); - - /// Returns the managed-settings authoring JSON schema with descriptive `x-composition` annotations aligned with the shared settings-engine vocabulary. These annotations are not a complete runtime composition contract: model, effortLevel, and contextTier remain coupled. Use `managedSettings.compose` for the runtime's effective result. Performs no I/O. - /// The to monitor for cancellation requests. The default is . - /// The authoring JSON schema for managed settings recognized by this runtime. - public async Task SchemaAsync(CancellationToken cancellationToken = default) - { - return await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.schema", [], cancellationToken); - } - - /// Validates a candidate managed-settings document the way the runtime validates delivered policy, without applying it. Reports errors that would reject the document, warnings for content the runtime ignores, and the canonical document it would apply. Document text nested more than 64 levels deep is rejected. Performs no I/O. - /// The document to validate: a JSON object, or a string containing the document's JSON text. Preview documents are limited to 1 MiB and 64 levels of nesting, a stricter resource limit than delivered-policy parsing; violations are returned as diagnostics. - /// Channel the document is meant for (`device`, `server`, or `policyHelper`). Some keys are only honored in some channels; for example, a `policyHelper` registration is ignored in policy-helper output. When omitted, no channel-specific checks run. - /// The to monitor for cancellation requests. The default is . - /// Result of validating a managed-settings document. - public async Task ValidateAsync(object content, string? layer = null, CancellationToken cancellationToken = default) - { - ArgumentNullException.ThrowIfNull(content); - - var request = new ManagedSettingsValidateRequest { Content = CopilotClient.ToJsonElementForWire(content)!.Value, Layer = layer }; - return await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.validate", [request], cancellationToken); - } - - /// Merges candidate managed-settings documents for the device, server, and policy-helper channels into the effective settings the runtime would enforce on this host, using the same precedence and composition rules as live resolution, without applying them. Like live resolution, a server's advisory sandbox force-enable is declined on a host that cannot run the sandbox. Does not fetch policy or read policy files, but may perform blocking OS or subprocess probes for sandbox support. Preview documents are limited to 1 MiB and 64 levels of nesting. - /// One entry per channel. `source` must be `device`, `server`, or `policyHelper`, each at most once (checked at runtime); order does not matter, because channel precedence is fixed. To preview documents from resolve output, map recognized source strings to ManagedSettingsChannel and copy their settings; generated resolve and compose layer types are distinct. Omitted settings means this channel delivered no document. Supplied documents must be valid within the preview limits; warnings are returned in diagnostics. Compose does not reproduce source-failure state or retained enforcement floors from resolve. - /// The to monitor for cancellation requests. The default is . - /// The effective managed settings the runtime would enforce for the given documents, in the same shape `managedSettings.resolve` returns. - public async Task ComposeAsync(IList layers, CancellationToken cancellationToken = default) - { - ArgumentNullException.ThrowIfNull(layers); - - var request = new ManagedSettingsComposeRequest { Layers = layers }; - return await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.compose", [request], cancellationToken); - } -} - /// Provides server-scoped Runtime APIs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed class ServerRuntimeApi @@ -46371,6 +50854,17 @@ public async Task GetEnforcementStatusAsync(Cancellati return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.sandbox.getEnforcementStatus", [request], cancellationToken); } + /// Lists possible secret-bearing environment variables available to new sandboxed shells that are not already covered by credential masking. Uses local name rules and known-provider host suggestions, never returns secret values, and changes no settings or network permissions. Returns no suggestions while the session is not sandboxed. + /// The to monitor for cancellation requests. The default is . + /// Possible unconfigured secrets in the sandbox shell environment, sorted by variable name, with no secret values. + public async Task GetCredentialSuggestionsAsync(CancellationToken cancellationToken = default) + { + _session.ThrowIfDisposed(); + + var request = new SessionSandboxGetCredentialSuggestionsRequest { SessionId = _session.SessionId }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.sandbox.getCredentialSuggestions", [request], cancellationToken); + } + /// Disables sandboxing for the remainder of the current session and approves the referenced pending sandbox-bypass permission request. The request is rejected unless the exact request is still pending and the effective sandbox policy permits bypass. /// Identifier of the exact pending sandbox-bypass permission request that authorized the session opt-out. /// Optional attribution for the permission decision. @@ -46787,14 +51281,13 @@ internal CanvasProviderApi(CopilotSession session) /// Provider metadata supplied by the host. /// Canvas contributions supplied by the provider. /// The to monitor for cancellation requests. The default is . - internal async Task RegisterAsync(string connectionId, object info, IList canvases, CancellationToken cancellationToken = default) + internal async Task RegisterAsync(string connectionId, object? info, IList canvases, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(connectionId); - ArgumentNullException.ThrowIfNull(info); ArgumentNullException.ThrowIfNull(canvases); _session.ThrowIfDisposed(); - var request = new CanvasProviderRegisterRequest { SessionId = _session.SessionId, ConnectionId = connectionId, Info = CopilotClient.ToJsonElementForWire(info)!.Value, Canvases = canvases.Select(static v => CopilotClient.ToJsonElementForWire(v)!.Value).ToList() }; + var request = new CanvasProviderRegisterRequest { SessionId = _session.SessionId, ConnectionId = connectionId, Info = CopilotClient.ToJsonElementForWire(info) ?? JsonElement.Parse("null"), Canvases = canvases.Select(static v => CopilotClient.ToJsonElementForWire(v)!.Value).ToList() }; await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.canvas.provider.register", [request], cancellationToken); } @@ -46828,13 +51321,12 @@ internal WorkflowApi(CopilotSession session) /// Workflow invocation options. /// The to monitor for cancellation requests. The default is . /// Complete current or terminal workflow run envelope. - public async Task RunAsync(string name, object args, WorkflowRunOptions? options = null, CancellationToken cancellationToken = default) + public async Task RunAsync(string name, object? args, WorkflowRunOptions? options = null, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(name); - ArgumentNullException.ThrowIfNull(args); _session.ThrowIfDisposed(); - var request = new WorkflowRunRequest { SessionId = _session.SessionId, Name = name, Args = CopilotClient.ToJsonElementForWire(args)!.Value, Options = options }; + var request = new WorkflowRunRequest { SessionId = _session.SessionId, Name = name, Args = CopilotClient.ToJsonElementForWire(args) ?? JsonElement.Parse("null"), Options = options }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.workflow.run", [request], cancellationToken); } @@ -46861,13 +51353,12 @@ public async Task ResumeAsync(string runId, WorkflowRunLim /// Opaque identifier of the originating tool call. /// The to monitor for cancellation requests. The default is . /// Complete current or terminal workflow run envelope. - internal async Task RunFromToolAsync(string name, object args, WorkflowToolRunOptions? options = null, string? toolCallId = null, CancellationToken cancellationToken = default) + internal async Task RunFromToolAsync(string name, object? args, WorkflowToolRunOptions? options = null, string? toolCallId = null, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(name); - ArgumentNullException.ThrowIfNull(args); _session.ThrowIfDisposed(); - var request = new WorkflowToolRunRequest { SessionId = _session.SessionId, Name = name, Args = CopilotClient.ToJsonElementForWire(args)!.Value, Options = options, ToolCallId = toolCallId }; + var request = new WorkflowToolRunRequest { SessionId = _session.SessionId, Name = name, Args = CopilotClient.ToJsonElementForWire(args) ?? JsonElement.Parse("null"), Options = options, ToolCallId = toolCallId }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.workflow.runFromTool", [request], cancellationToken); } @@ -47063,15 +51554,14 @@ public async Task GetAsync(string runId, string execut /// JSON result to memoize. /// The to monitor for cancellation requests. The default is . /// Acknowledgement that a workflow request was accepted. - public async Task PutAsync(string runId, string executionToken, string key, object resultJson, CancellationToken cancellationToken = default) + public async Task PutAsync(string runId, string executionToken, string key, object? resultJson, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(runId); ArgumentNullException.ThrowIfNull(executionToken); ArgumentNullException.ThrowIfNull(key); - ArgumentNullException.ThrowIfNull(resultJson); _session.ThrowIfDisposed(); - var request = new WorkflowJournalPutRequest { SessionId = _session.SessionId, RunId = runId, ExecutionToken = executionToken, Key = key, ResultJson = CopilotClient.ToJsonElementForWire(resultJson)!.Value }; + var request = new WorkflowJournalPutRequest { SessionId = _session.SessionId, RunId = runId, ExecutionToken = executionToken, Key = key, ResultJson = CopilotClient.ToJsonElementForWire(resultJson) ?? JsonElement.Parse("null") }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.workflow.journal.put", [request], cancellationToken); } } @@ -47213,6 +51703,19 @@ public async Task SetReasoningEffortAsync(string return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.model.setReasoningEffort", [request], cancellationToken); } + /// Computes whether the session's current model shows reasoning summaries and how a host should present them: the default Ctrl+T visibility, reasoning headers, the hidden-summaries hint, and whether a persisted `showReasoning` setting is redundant. The runtime folds in the session's model catalog, selection and reasoning-summaries experiment assignment; the host supplies only its own persisted setting and current visibility. This method reads the session's resolved model catalog rather than resolving it, so call `session.model.list` first on a session that has not listed models or run a turn; until then the result is `ready: false`. A session that resolves models without a Copilot catalog, that is a BYOK registry or an alternate provider, never caches one and stays `ready: false`. + /// The host's persisted `showReasoning` user setting, omitted when unset. + /// Whether the host is currently showing reasoning summaries. Defaults to false. + /// The to monitor for cancellation requests. The default is . + /// Reasoning-summaries presentation policy for the session's current model. + public async Task GetReasoningSummariesAsync(bool? persistedShowReasoning = null, bool? summariesVisible = null, CancellationToken cancellationToken = default) + { + _session.ThrowIfDisposed(); + + var request = new ModelGetReasoningSummariesRequest { SessionId = _session.SessionId, PersistedShowReasoning = persistedShowReasoning, SummariesVisible = summariesVisible }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.model.getReasoningSummaries", [request], cancellationToken); + } + /// 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. /// Optional listing options. /// The to monitor for cancellation requests. The default is . @@ -48301,15 +52804,13 @@ internal async Task ReloadWithConfigAsync(CancellationTok /// Raw MCP CreateMessageRequest params, as received in the `sampling.requested` event. Treated as opaque at the schema layer; the runtime converts the embedded MCP messages into the OpenAI chat-completion shape internally. /// The to monitor for cancellation requests. The default is . /// Outcome of an MCP sampling execution: success result, failure error, or cancellation. - public async Task ExecuteSamplingAsync(string requestId, string serverName, object mcpRequestId, McpExecuteSamplingRequest request, CancellationToken cancellationToken = default) + public async Task ExecuteSamplingAsync(string requestId, string serverName, object? mcpRequestId, object? request, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(requestId); ArgumentNullException.ThrowIfNull(serverName); - ArgumentNullException.ThrowIfNull(mcpRequestId); - ArgumentNullException.ThrowIfNull(request); _session.ThrowIfDisposed(); - var rpcRequest = new McpExecuteSamplingParams { SessionId = _session.SessionId, RequestId = requestId, ServerName = serverName, McpRequestId = CopilotClient.ToJsonElementForWire(mcpRequestId)!.Value, Request = request }; + var rpcRequest = new McpExecuteSamplingParams { SessionId = _session.SessionId, RequestId = requestId, ServerName = serverName, McpRequestId = CopilotClient.ToJsonElementForWire(mcpRequestId) ?? JsonElement.Parse("null"), Request = CopilotClient.ToJsonElementForWire(request) ?? JsonElement.Parse("null") }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.mcp.executeSampling", [rpcRequest], cancellationToken); } @@ -49223,6 +53724,18 @@ public async Task ReloadAsync(SessionPluginsReloadRequest? request = null, Cance await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.plugins.reload", [rpcRequest], cancellationToken); } + /// Installs or updates the plugins the session's managed settings require now, without waiting for the next automatic attempt after a failure, then reloads plugins. Reports progress and failures with the same `session.info` and `session.warning` events as automatic preparation. Fails when the runtime does not prepare managed plugins for the session. + /// Plugins required by the session's managed settings to install or update again. + /// The to monitor for cancellation requests. The default is . + /// Per-plugin outcomes of retrying the plugins required by the session's managed settings. + public async Task RetryManagedAsync(SessionPluginsRetryManagedRequest? request = null, CancellationToken cancellationToken = default) + { + _session.ThrowIfDisposed(); + + var rpcRequest = new SessionPluginsRetryManagedRequestWithSession { SessionId = _session.SessionId, Plugins = request?.Plugins }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.plugins.retryManaged", [rpcRequest], cancellationToken); + } + /// Marketplaces APIs. public PluginsMarketplacesApi Marketplaces => field ?? @@ -49408,7 +53921,10 @@ internal OptionsApi(CopilotSession session) /// Whether interactive shell sessions are logged. /// How env values are passed to MCP servers (`direct` inlines literal values; `indirect` resolves at launch). /// Whether to include instructions from every MCP server in the system prompt instead of only allowlisted servers. - /// Additional directories to search for skills. + /// Explicit discovery-only skill roots, not filesystem grants. Available without enableConfigDiscovery, subject to skill/managed disables. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. + /// Explicit discovery-only agent roots, separate from customAgents and filesystem grants. Available without enableConfigDiscovery, subject to managed/source disables. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. + /// Explicit discovery-only instruction roots using existing SDK instruction formats, not filesystem grants. Available without enableConfigDiscovery, subject to instruction/managed disables. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. + /// Explicit discovery-only file-hook roots, separate from callback hooks and filesystem grants. Available without enableConfigDiscovery or enableFileHooks, which controls repository-origin hooks only; hook/managed disables still apply. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. /// Skill scan directories and descendants excluded from discovery. Supports `~`-relative paths. /// Built-in skill names to include in this session. When specified, only these runtime-bundled skills are available. Skills from other sources with the same name remain available. Set to null to remove the allowlist restriction. /// Skill IDs that should be excluded from this session. @@ -49435,19 +53951,19 @@ internal OptionsApi(CopilotSession session) /// Replaces the session's capability set with the given list. Use to enable or disable capabilities mid-session (e.g., remove `memory` for reproducible scripted runs). Omit the field to leave the existing capability set unchanged. /// Whether to skip embedding retrieval pipeline initialization and execution. /// Organization-level custom instructions to inject into the system prompt. - /// Whether to enable loading of `.github/hooks/` filesystem hooks. Separate from the SDK callback hook mechanism. + /// Whether to enable repository-origin file hooks from `.github/hooks/` and trusted repository-configured roots. Does not control explicit hookDirectories or SDK callback hooks. /// Whether to enable host git operations (context resolution, child repo scanning, git info in system prompt). /// Whether to enable cross-session store writes and reads. - /// Whether skill loading is enabled. Explicit false disables every source, including a bound SDK provider; changing the value invalidates the loaded skill snapshot. When omitted, creation falls back to enableConfigDiscovery unless an SDK skill provider is registered. + /// Whether skill loading is enabled. Explicit false disables every source, including explicit SDK roots and a bound SDK provider; changing the value invalidates the loaded skill snapshot. When omitted, creation falls back to enableConfigDiscovery unless explicit SDK skill roots or an SDK skill provider are supplied. /// Context tier for models with tiered pricing. The session uses this to derive effective `modelCapabilitiesOverrides` so compaction, truncation, token display, and request limits honor the selected tier. /// Optional session limits. Pass null to clear the session limits. /// The to monitor for cancellation requests. The default is . /// Indicates whether the session options patch was applied successfully. - public async Task UpdateAsync(string? model = null, ModelCapabilitiesOverride? modelCapabilitiesOverrides = null, string? reasoningEffort = null, OptionsUpdateReasoningSummary? reasoningSummary = null, Verbosity? verbosity = null, string? clientName = null, string? lspClientName = null, string? integrationId = null, IDictionary? featureFlags = null, bool? isExperimentalMode = null, ProviderConfig? provider = null, CapiSessionOptions? capi = null, string? workingDirectory = null, IList? availableTools = null, IList? excludedTools = null, IList? includedBuiltinAgents = null, IList? excludedBuiltinAgents = null, OptionsUpdateToolFilterPrecedence? toolFilterPrecedence = null, bool? enableScriptSafety = null, ShellOptions? shell = null, string? shellInitProfile = null, IList? shellProcessFlags = null, SandboxConfig? sandboxConfig = null, SandboxConfigSource? sandboxConfigSource = null, bool? logInteractiveShells = null, OptionsUpdateEnvValueMode? envValueMode = null, bool? allowAllMcpServerInstructions = null, IList? skillDirectories = null, IList? ignoredSkillsLocations = null, IList? includedBuiltinSkills = null, IList? disabledSkills = null, bool? enableOnDemandInstructionDiscovery = null, long? maxInlineBinaryBytes = null, IList? installedPlugins = null, bool? customAgentsLocalOnly = null, bool? suppressCustomAgentPrompt = null, bool? skipCustomInstructions = null, IList? disabledInstructionSources = null, bool? coauthorEnabled = null, string? trajectoryFile = null, bool? enableStreaming = null, string? copilotUrl = null, bool? askUserDisabled = null, bool? continueOnAutoMode = null, bool? runningInInteractiveMode = null, bool? enableReasoningSummaries = null, string? agentContext = null, string? eventsLogDirectory = null, bool? eventsLogIncludesSubagents = null, IList? additionalContentExclusionPolicies = null, bool? manageScheduleEnabled = null, IList? sessionCapabilities = null, bool? skipEmbeddingRetrieval = null, string? organizationCustomInstructions = null, bool? enableFileHooks = null, bool? enableHostGitOperations = null, bool? enableSessionStore = null, bool? enableSkills = null, OptionsUpdateContextTier? contextTier = null, SessionLimitsConfig? sessionLimits = null, CancellationToken cancellationToken = default) + public async Task UpdateAsync(string? model = null, ModelCapabilitiesOverride? modelCapabilitiesOverrides = null, string? reasoningEffort = null, OptionsUpdateReasoningSummary? reasoningSummary = null, Verbosity? verbosity = null, string? clientName = null, string? lspClientName = null, string? integrationId = null, IDictionary? featureFlags = null, bool? isExperimentalMode = null, ProviderConfig? provider = null, CapiSessionOptions? capi = null, string? workingDirectory = null, IList? availableTools = null, IList? excludedTools = null, IList? includedBuiltinAgents = null, IList? excludedBuiltinAgents = null, OptionsUpdateToolFilterPrecedence? toolFilterPrecedence = null, bool? enableScriptSafety = null, ShellOptions? shell = null, string? shellInitProfile = null, IList? shellProcessFlags = null, SandboxConfig? sandboxConfig = null, SandboxConfigSource? sandboxConfigSource = null, bool? logInteractiveShells = null, OptionsUpdateEnvValueMode? envValueMode = null, bool? allowAllMcpServerInstructions = null, IList? skillDirectories = null, IList? agentDirectories = null, IList? instructionDirectories = null, IList? hookDirectories = null, IList? ignoredSkillsLocations = null, IList? includedBuiltinSkills = null, IList? disabledSkills = null, bool? enableOnDemandInstructionDiscovery = null, long? maxInlineBinaryBytes = null, IList? installedPlugins = null, bool? customAgentsLocalOnly = null, bool? suppressCustomAgentPrompt = null, bool? skipCustomInstructions = null, IList? disabledInstructionSources = null, bool? coauthorEnabled = null, string? trajectoryFile = null, bool? enableStreaming = null, string? copilotUrl = null, bool? askUserDisabled = null, bool? continueOnAutoMode = null, bool? runningInInteractiveMode = null, bool? enableReasoningSummaries = null, string? agentContext = null, string? eventsLogDirectory = null, bool? eventsLogIncludesSubagents = null, IList? additionalContentExclusionPolicies = null, bool? manageScheduleEnabled = null, IList? sessionCapabilities = null, bool? skipEmbeddingRetrieval = null, string? organizationCustomInstructions = null, bool? enableFileHooks = null, bool? enableHostGitOperations = null, bool? enableSessionStore = null, bool? enableSkills = null, OptionsUpdateContextTier? contextTier = null, SessionLimitsConfig? sessionLimits = null, CancellationToken cancellationToken = default) { _session.ThrowIfDisposed(); - var request = new SessionUpdateOptionsParams { SessionId = _session.SessionId, Model = model, ModelCapabilitiesOverrides = modelCapabilitiesOverrides, ReasoningEffort = reasoningEffort, ReasoningSummary = reasoningSummary, Verbosity = verbosity, ClientName = clientName, LspClientName = lspClientName, IntegrationId = integrationId, FeatureFlags = featureFlags, IsExperimentalMode = isExperimentalMode, Provider = provider, Capi = capi, WorkingDirectory = workingDirectory, AvailableTools = availableTools, ExcludedTools = excludedTools, IncludedBuiltinAgents = includedBuiltinAgents, ExcludedBuiltinAgents = excludedBuiltinAgents, ToolFilterPrecedence = toolFilterPrecedence, EnableScriptSafety = enableScriptSafety, Shell = shell, ShellInitProfile = shellInitProfile, ShellProcessFlags = shellProcessFlags, SandboxConfig = sandboxConfig, SandboxConfigSource = sandboxConfigSource, LogInteractiveShells = logInteractiveShells, EnvValueMode = envValueMode, AllowAllMcpServerInstructions = allowAllMcpServerInstructions, SkillDirectories = skillDirectories, IgnoredSkillsLocations = ignoredSkillsLocations, IncludedBuiltinSkills = includedBuiltinSkills, DisabledSkills = disabledSkills, EnableOnDemandInstructionDiscovery = enableOnDemandInstructionDiscovery, MaxInlineBinaryBytes = maxInlineBinaryBytes, InstalledPlugins = installedPlugins, CustomAgentsLocalOnly = customAgentsLocalOnly, SuppressCustomAgentPrompt = suppressCustomAgentPrompt, SkipCustomInstructions = skipCustomInstructions, DisabledInstructionSources = disabledInstructionSources, CoauthorEnabled = coauthorEnabled, TrajectoryFile = trajectoryFile, EnableStreaming = enableStreaming, CopilotUrl = copilotUrl, AskUserDisabled = askUserDisabled, ContinueOnAutoMode = continueOnAutoMode, RunningInInteractiveMode = runningInInteractiveMode, EnableReasoningSummaries = enableReasoningSummaries, AgentContext = agentContext, EventsLogDirectory = eventsLogDirectory, EventsLogIncludesSubagents = eventsLogIncludesSubagents, AdditionalContentExclusionPolicies = additionalContentExclusionPolicies, ManageScheduleEnabled = manageScheduleEnabled, SessionCapabilities = sessionCapabilities, SkipEmbeddingRetrieval = skipEmbeddingRetrieval, OrganizationCustomInstructions = organizationCustomInstructions, EnableFileHooks = enableFileHooks, EnableHostGitOperations = enableHostGitOperations, EnableSessionStore = enableSessionStore, EnableSkills = enableSkills, ContextTier = contextTier, SessionLimits = sessionLimits }; + var request = new SessionUpdateOptionsParams { SessionId = _session.SessionId, Model = model, ModelCapabilitiesOverrides = modelCapabilitiesOverrides, ReasoningEffort = reasoningEffort, ReasoningSummary = reasoningSummary, Verbosity = verbosity, ClientName = clientName, LspClientName = lspClientName, IntegrationId = integrationId, FeatureFlags = featureFlags, IsExperimentalMode = isExperimentalMode, Provider = provider, Capi = capi, WorkingDirectory = workingDirectory, AvailableTools = availableTools, ExcludedTools = excludedTools, IncludedBuiltinAgents = includedBuiltinAgents, ExcludedBuiltinAgents = excludedBuiltinAgents, ToolFilterPrecedence = toolFilterPrecedence, EnableScriptSafety = enableScriptSafety, Shell = shell, ShellInitProfile = shellInitProfile, ShellProcessFlags = shellProcessFlags, SandboxConfig = sandboxConfig, SandboxConfigSource = sandboxConfigSource, LogInteractiveShells = logInteractiveShells, EnvValueMode = envValueMode, AllowAllMcpServerInstructions = allowAllMcpServerInstructions, SkillDirectories = skillDirectories, AgentDirectories = agentDirectories, InstructionDirectories = instructionDirectories, HookDirectories = hookDirectories, IgnoredSkillsLocations = ignoredSkillsLocations, IncludedBuiltinSkills = includedBuiltinSkills, DisabledSkills = disabledSkills, EnableOnDemandInstructionDiscovery = enableOnDemandInstructionDiscovery, MaxInlineBinaryBytes = maxInlineBinaryBytes, InstalledPlugins = installedPlugins, CustomAgentsLocalOnly = customAgentsLocalOnly, SuppressCustomAgentPrompt = suppressCustomAgentPrompt, SkipCustomInstructions = skipCustomInstructions, DisabledInstructionSources = disabledInstructionSources, CoauthorEnabled = coauthorEnabled, TrajectoryFile = trajectoryFile, EnableStreaming = enableStreaming, CopilotUrl = copilotUrl, AskUserDisabled = askUserDisabled, ContinueOnAutoMode = continueOnAutoMode, RunningInInteractiveMode = runningInInteractiveMode, EnableReasoningSummaries = enableReasoningSummaries, AgentContext = agentContext, EventsLogDirectory = eventsLogDirectory, EventsLogIncludesSubagents = eventsLogIncludesSubagents, AdditionalContentExclusionPolicies = additionalContentExclusionPolicies, ManageScheduleEnabled = manageScheduleEnabled, SessionCapabilities = sessionCapabilities, SkipEmbeddingRetrieval = skipEmbeddingRetrieval, OrganizationCustomInstructions = organizationCustomInstructions, EnableFileHooks = enableFileHooks, EnableHostGitOperations = enableHostGitOperations, EnableSessionStore = enableSessionStore, EnableSkills = enableSkills, ContextTier = contextTier, SessionLimits = sessionLimits }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.options.update", [request], cancellationToken); } } @@ -49575,13 +54091,12 @@ internal ToolsApi(CopilotSession session) /// Optional identifier used to correlate this invocation with its tool call. /// The to monitor for cancellation requests. The default is . /// Canonical result returned by a session tool. - public async Task ExecuteAsync(string name, object arguments, string? toolCallId = null, CancellationToken cancellationToken = default) + public async Task ExecuteAsync(string name, object? arguments, string? toolCallId = null, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(name); - ArgumentNullException.ThrowIfNull(arguments); _session.ThrowIfDisposed(); - var request = new ToolsExecuteRequest { SessionId = _session.SessionId, Name = name, Arguments = CopilotClient.ToJsonElementForWire(arguments)!.Value, ToolCallId = toolCallId }; + var request = new ToolsExecuteRequest { SessionId = _session.SessionId, Name = name, Arguments = CopilotClient.ToJsonElementForWire(arguments) ?? JsonElement.Parse("null"), ToolCallId = toolCallId }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.tools.execute", [request], cancellationToken); } @@ -49608,13 +54123,12 @@ public async Task GetBuiltinDescriptorsAsync(b /// Final expanded result returned by the task_complete tool. /// The to monitor for cancellation requests. The default is . /// Task completion notification with summary from the agent. - public async Task TaskCompleteEventDataAsync(object toolArgs, ToolResultExpanded finalResult, CancellationToken cancellationToken = default) + public async Task TaskCompleteEventDataAsync(object? toolArgs, ToolResultExpanded finalResult, CancellationToken cancellationToken = default) { - ArgumentNullException.ThrowIfNull(toolArgs); ArgumentNullException.ThrowIfNull(finalResult); _session.ThrowIfDisposed(); - var request = new ToolsTaskCompleteEventDataRequest { SessionId = _session.SessionId, ToolArgs = CopilotClient.ToJsonElementForWire(toolArgs)!.Value, FinalResult = finalResult }; + var request = new ToolsTaskCompleteEventDataRequest { SessionId = _session.SessionId, ToolArgs = CopilotClient.ToJsonElementForWire(toolArgs) ?? JsonElement.Parse("null"), FinalResult = finalResult }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.tools.taskCompleteEventData", [request], cancellationToken); } @@ -49735,7 +54249,7 @@ public async Task InvokeAsync(string name, string? /// Whether the host applied or cancelled the pending invocation effect. /// The to monitor for cancellation requests. The default is . /// Whether finalizing the invocation effect succeeded, and the failure reason when it did not. - internal async Task FinalizeInvocationEffectAsync(CommandsFinalizeInvocationEffectRequestEffect effect, CommandsInvocationEffectOutcome outcome, CancellationToken cancellationToken = default) + internal async Task FinalizeInvocationEffectAsync(IDictionary effect, CommandsInvocationEffectOutcome outcome, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(effect); _session.ThrowIfDisposed(); @@ -49945,12 +54459,12 @@ internal async Task HandleHumanUserInputAsync(string requ /// Optional sampling result payload. Omit to reject/cancel the sampling request without providing a result. /// The to monitor for cancellation requests. The default is . /// Indicates whether the pending UI request was resolved by this call. - public async Task HandlePendingSamplingAsync(string requestId, UIHandlePendingSamplingResponse? response = null, CancellationToken cancellationToken = default) + public async Task HandlePendingSamplingAsync(string requestId, object? response = null, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(requestId); _session.ThrowIfDisposed(); - var request = new UIHandlePendingSamplingRequest { SessionId = _session.SessionId, RequestId = requestId, Response = response }; + var request = new UIHandlePendingSamplingRequest { SessionId = _session.SessionId, RequestId = requestId, Response = CopilotClient.ToJsonElementForWire(response) }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.ui.handlePendingSampling", [request], cancellationToken); } @@ -51032,12 +55546,11 @@ public async Task ClearAsync(CancellationToken cancellationToken = default) /// Opaque runtime-owned filter object. /// The to monitor for cancellation requests. The default is . /// Indicates whether a user-facing pending item was removed. - internal async Task ConsumeSystemNotificationsAsync(object filter, CancellationToken cancellationToken = default) + internal async Task ConsumeSystemNotificationsAsync(object? filter, CancellationToken cancellationToken = default) { - ArgumentNullException.ThrowIfNull(filter); _session.ThrowIfDisposed(); - var request = new QueueConsumeSystemNotificationsRequest { SessionId = _session.SessionId, Filter = CopilotClient.ToJsonElementForWire(filter)!.Value }; + var request = new QueueConsumeSystemNotificationsRequest { SessionId = _session.SessionId, Filter = CopilotClient.ToJsonElementForWire(filter) ?? JsonElement.Parse("null") }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.queue.consumeSystemNotifications", [request], cancellationToken); } @@ -52081,6 +56594,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.IndexedSearchIncrementalPhase), TypeInfoPropertyName = "SessionEventsIndexedSearchIncrementalPhase")] [JsonSerializable(typeof(GitHub.Copilot.IndexedSearchOutcome), TypeInfoPropertyName = "SessionEventsIndexedSearchOutcome")] [JsonSerializable(typeof(GitHub.Copilot.IndexedSearchState), TypeInfoPropertyName = "SessionEventsIndexedSearchState")] +[JsonSerializable(typeof(GitHub.Copilot.ManagedPermissionsContext), TypeInfoPropertyName = "SessionEventsManagedPermissionsContext")] [JsonSerializable(typeof(GitHub.Copilot.ManagedPluginProgressPhase), TypeInfoPropertyName = "SessionEventsManagedPluginProgressPhase")] [JsonSerializable(typeof(GitHub.Copilot.ManagedSettingsEnforcedAction), TypeInfoPropertyName = "SessionEventsManagedSettingsEnforcedAction")] [JsonSerializable(typeof(GitHub.Copilot.ManagedSettingsEnforcedEscalation), TypeInfoPropertyName = "SessionEventsManagedSettingsEnforcedEscalation")] @@ -52126,8 +56640,10 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.ModelCallFinishedData), TypeInfoPropertyName = "SessionEventsModelCallFinishedData")] [JsonSerializable(typeof(GitHub.Copilot.ModelCallFinishedEvent), TypeInfoPropertyName = "SessionEventsModelCallFinishedEvent")] [JsonSerializable(typeof(GitHub.Copilot.ModelCallFinishedOutcome), TypeInfoPropertyName = "SessionEventsModelCallFinishedOutcome")] +[JsonSerializable(typeof(GitHub.Copilot.ModelCallRequestBodyEncoding), TypeInfoPropertyName = "SessionEventsModelCallRequestBodyEncoding")] [JsonSerializable(typeof(GitHub.Copilot.ModelCallStartData), TypeInfoPropertyName = "SessionEventsModelCallStartData")] [JsonSerializable(typeof(GitHub.Copilot.ModelCallStartEvent), TypeInfoPropertyName = "SessionEventsModelCallStartEvent")] +[JsonSerializable(typeof(GitHub.Copilot.ModelCallWebSocketFallbackErrorKind), TypeInfoPropertyName = "SessionEventsModelCallWebSocketFallbackErrorKind")] [JsonSerializable(typeof(GitHub.Copilot.ModelCallWebSocketFallbackReason), TypeInfoPropertyName = "SessionEventsModelCallWebSocketFallbackReason")] [JsonSerializable(typeof(GitHub.Copilot.ModelChangeSource), TypeInfoPropertyName = "SessionEventsModelChangeSource")] [JsonSerializable(typeof(GitHub.Copilot.ModelDeselectedReason), TypeInfoPropertyName = "SessionEventsModelDeselectedReason")] @@ -52214,6 +56730,9 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.PlanChangedOperation), TypeInfoPropertyName = "SessionEventsPlanChangedOperation")] [JsonSerializable(typeof(GitHub.Copilot.PromptCacheBreakData), TypeInfoPropertyName = "SessionEventsPromptCacheBreakData")] [JsonSerializable(typeof(GitHub.Copilot.PromptCacheBreakEvent), TypeInfoPropertyName = "SessionEventsPromptCacheBreakEvent")] +[JsonSerializable(typeof(GitHub.Copilot.ProviderMonthlyUsage), TypeInfoPropertyName = "SessionEventsProviderMonthlyUsage")] +[JsonSerializable(typeof(GitHub.Copilot.ProviderMonthlyUsageScope), TypeInfoPropertyName = "SessionEventsProviderMonthlyUsageScope")] +[JsonSerializable(typeof(GitHub.Copilot.ProviderMonthlyUsageState), TypeInfoPropertyName = "SessionEventsProviderMonthlyUsageState")] [JsonSerializable(typeof(GitHub.Copilot.ProviderQuotaAccessState), TypeInfoPropertyName = "SessionEventsProviderQuotaAccessState")] [JsonSerializable(typeof(GitHub.Copilot.ProviderQuotaAcquisitionStatus), TypeInfoPropertyName = "SessionEventsProviderQuotaAcquisitionStatus")] [JsonSerializable(typeof(GitHub.Copilot.ProviderQuotaBudgetMetadata), TypeInfoPropertyName = "SessionEventsProviderQuotaBudgetMetadata")] @@ -52405,9 +56924,16 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(AbortRequest))] [JsonSerializable(typeof(AbortResult))] [JsonSerializable(typeof(AccountAllUsers))] +[JsonSerializable(typeof(AccountDescribeAuthInfoRequest))] +[JsonSerializable(typeof(AccountDescribeAuthInfoResult))] [JsonSerializable(typeof(AccountGetCurrentAuthResult))] +[JsonSerializable(typeof(AccountGetGistSupportRequest))] +[JsonSerializable(typeof(AccountGetGistSupportResult))] [JsonSerializable(typeof(AccountGetQuotaRequest))] [JsonSerializable(typeof(AccountGetQuotaResult))] +[JsonSerializable(typeof(AccountGetTokenEnvVarResult))] +[JsonSerializable(typeof(AccountGetWebUrlRequest))] +[JsonSerializable(typeof(AccountGetWebUrlResult))] [JsonSerializable(typeof(AccountLoginRequest))] [JsonSerializable(typeof(AccountLoginResult))] [JsonSerializable(typeof(AccountLogoutRequest))] @@ -52510,7 +57036,6 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(ClientTaskCancelResult))] [JsonSerializable(typeof(CommandList))] [JsonSerializable(typeof(CommandsFinalizeInvocationEffectRequest))] -[JsonSerializable(typeof(CommandsFinalizeInvocationEffectRequestEffect))] [JsonSerializable(typeof(CommandsFinalizeInvocationEffectResult))] [JsonSerializable(typeof(CommandsHandlePendingCommandRequest))] [JsonSerializable(typeof(CommandsHandlePendingCommandResult))] @@ -52612,11 +57137,17 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(ExternalToolTextResultForLlmBinaryResultsForLlm))] [JsonSerializable(typeof(ExternalToolTextResultForLlmContent))] [JsonSerializable(typeof(ExternalToolTextResultForLlmContentResourceLinkIcon))] +[JsonSerializable(typeof(FeatureFlagMetadata))] +[JsonSerializable(typeof(FeatureFlagsMetadataResult))] +[JsonSerializable(typeof(FeatureFlagsResolveRequest))] +[JsonSerializable(typeof(FeatureFlagsResolveStartupGatesRequest))] [JsonSerializable(typeof(FleetStartRequest))] [JsonSerializable(typeof(FleetStartResult))] [JsonSerializable(typeof(FolderTrustAddParams))] [JsonSerializable(typeof(FolderTrustCheckParams))] [JsonSerializable(typeof(FolderTrustCheckResult))] +[JsonSerializable(typeof(FolderTrustIsTrustedParams))] +[JsonSerializable(typeof(ForceLoginOrgsDecision))] [JsonSerializable(typeof(GitCurrentBranchRemoteResult))] [JsonSerializable(typeof(GitCwdRequest))] [JsonSerializable(typeof(GitHubEnvironment))] @@ -52683,6 +57214,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(HostStartRequest))] [JsonSerializable(typeof(HostStartResult))] [JsonSerializable(typeof(IDictionary))] +[JsonSerializable(typeof(IDictionary))] [JsonSerializable(typeof(IDictionary))] [JsonSerializable(typeof(IList))] [JsonSerializable(typeof(IList))] @@ -52698,9 +57230,11 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(InstructionDiscoveryPath))] [JsonSerializable(typeof(InstructionDiscoveryPathList))] [JsonSerializable(typeof(InstructionSource))] +[JsonSerializable(typeof(InstructionsDiscoverRepoSourcesRequest))] [JsonSerializable(typeof(InstructionsDiscoverRequest))] [JsonSerializable(typeof(InstructionsGetDiscoveryPathsRequest))] [JsonSerializable(typeof(InstructionsGetSourcesResult))] +[JsonSerializable(typeof(InstructionsInitPromptResult))] [JsonSerializable(typeof(InterruptMainTurnRequest))] [JsonSerializable(typeof(InterruptMainTurnResult))] [JsonSerializable(typeof(JsonSchemaResponseFormat))] @@ -52715,21 +57249,45 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(LlmInferenceHttpResponseStartResult))] [JsonSerializable(typeof(LlmInferenceSetProviderResult))] [JsonSerializable(typeof(LocalSessionMetadataValue))] +[JsonSerializable(typeof(LocationPermissionsCleanupResult))] [JsonSerializable(typeof(LogRequest))] [JsonSerializable(typeof(LogResult))] [JsonSerializable(typeof(LoggedInUser))] [JsonSerializable(typeof(LspInitializeRequest))] +[JsonSerializable(typeof(ManagedPermissionEvaluation))] +[JsonSerializable(typeof(ManagedPermissionOperation))] +[JsonSerializable(typeof(ManagedPluginRetryEntry))] [JsonSerializable(typeof(ManagedSettingMeta))] +[JsonSerializable(typeof(ManagedSettingsApplyControlsParams))] +[JsonSerializable(typeof(ManagedSettingsApplyControlsResult))] [JsonSerializable(typeof(ManagedSettingsComposeLayer))] [JsonSerializable(typeof(ManagedSettingsComposeRequest))] [JsonSerializable(typeof(ManagedSettingsComposeResult))] [JsonSerializable(typeof(ManagedSettingsDiagnostic))] +[JsonSerializable(typeof(ManagedSettingsIngestParams))] +[JsonSerializable(typeof(ManagedSettingsIngestResult))] [JsonSerializable(typeof(ManagedSettingsLayer))] +[JsonSerializable(typeof(ManagedSettingsLoadRemoteControlParams))] +[JsonSerializable(typeof(ManagedSettingsLoadRemoteControlResult))] +[JsonSerializable(typeof(ManagedSettingsLogMessage))] [JsonSerializable(typeof(ManagedSettingsMeta))] +[JsonSerializable(typeof(ManagedSettingsParseClientValueParams))] +[JsonSerializable(typeof(ManagedSettingsParseClientValueResult))] +[JsonSerializable(typeof(ManagedSettingsPermissionsEvaluateRequest))] +[JsonSerializable(typeof(ManagedSettingsPermissionsEvaluateResult))] +[JsonSerializable(typeof(ManagedSettingsReadDeviceResult))] [JsonSerializable(typeof(ManagedSettingsReadResult))] +[JsonSerializable(typeof(ManagedSettingsResolveBypassDisableTransitionParams))] +[JsonSerializable(typeof(ManagedSettingsResolveBypassDisableTransitionResult))] +[JsonSerializable(typeof(ManagedSettingsResolveDeviceForceLoginOrgsGateParams))] +[JsonSerializable(typeof(ManagedSettingsResolveEffectiveValueParams))] +[JsonSerializable(typeof(ManagedSettingsResolveEffectiveValueResult))] [JsonSerializable(typeof(ManagedSettingsResolveRequest))] [JsonSerializable(typeof(ManagedSettingsResolveResult))] +[JsonSerializable(typeof(ManagedSettingsResolveStartupPolicyParams))] +[JsonSerializable(typeof(ManagedSettingsResolveStartupPolicyResult))] [JsonSerializable(typeof(ManagedSettingsResolvedData))] +[JsonSerializable(typeof(ManagedSettingsRetainPolicyParams))] [JsonSerializable(typeof(ManagedSettingsSchemaResult))] [JsonSerializable(typeof(ManagedSettingsValidateRequest))] [JsonSerializable(typeof(ManagedSettingsValidateResult))] @@ -52781,8 +57339,6 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(McpEnableRequest))] [JsonSerializable(typeof(McpEnableRequestWithSession))] [JsonSerializable(typeof(McpExecuteSamplingParams))] -[JsonSerializable(typeof(McpExecuteSamplingRequest))] -[JsonSerializable(typeof(McpExecuteSamplingResult))] [JsonSerializable(typeof(McpFailedServer))] [JsonSerializable(typeof(McpFilteredServer))] [JsonSerializable(typeof(McpHeadersHandlePendingHeadersRefreshRequest))] @@ -52804,6 +57360,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(McpInstallationsRequest))] [JsonSerializable(typeof(McpIsServerRunningRequest))] [JsonSerializable(typeof(McpIsServerRunningResult))] +[JsonSerializable(typeof(McpListBuiltinServerNamesResult))] [JsonSerializable(typeof(McpListToolsRequest))] [JsonSerializable(typeof(McpListToolsResult))] [JsonSerializable(typeof(McpOauthAuthenticationStateChangedRequest))] @@ -52842,6 +57399,11 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(McpPromptsListRequest))] [JsonSerializable(typeof(McpPromptsListResult))] [JsonSerializable(typeof(McpRegisterExternalClientRequest))] +[JsonSerializable(typeof(McpRegistryCancelRequest))] +[JsonSerializable(typeof(McpRegistryCancelResult))] +[JsonSerializable(typeof(McpRegistryRequestIdResult))] +[JsonSerializable(typeof(McpRegistrySearchRequest))] +[JsonSerializable(typeof(McpRegistrySearchResult))] [JsonSerializable(typeof(McpReloadWithConfigRequest))] [JsonSerializable(typeof(McpRemoveGitHubResult))] [JsonSerializable(typeof(McpResource))] @@ -52866,6 +57428,8 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(McpServerOwnership))] [JsonSerializable(typeof(McpSetEnvValueModeParams))] [JsonSerializable(typeof(McpSetEnvValueModeResult))] +[JsonSerializable(typeof(McpShouldExcludeGitHubToolsRequest))] +[JsonSerializable(typeof(McpShouldExcludeGitHubToolsResult))] [JsonSerializable(typeof(McpSourceFile))] [JsonSerializable(typeof(McpSourcePlugin))] [JsonSerializable(typeof(McpSourceRef))] @@ -52916,11 +57480,12 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(ModelCapabilitiesOverrideLimitsVision))] [JsonSerializable(typeof(ModelCapabilitiesOverrideSupports))] [JsonSerializable(typeof(ModelCapabilitiesSupports))] +[JsonSerializable(typeof(ModelGetReasoningSummariesRequest))] +[JsonSerializable(typeof(ModelGetReasoningSummariesResult))] [JsonSerializable(typeof(ModelList))] [JsonSerializable(typeof(ModelMessage))] [JsonSerializable(typeof(ModelPickerPersistenceRequest))] [JsonSerializable(typeof(ModelPickerSettingsContext))] -[JsonSerializable(typeof(ModelPickerSettingsContextEnvironment))] [JsonSerializable(typeof(ModelPolicy))] [JsonSerializable(typeof(ModelProviderAdapterCatalog))] [JsonSerializable(typeof(ModelProviderAdapterDescriptor))] @@ -52994,6 +57559,8 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(PermissionsConfigureAdditionalContentExclusionPolicyRuleSource))] [JsonSerializable(typeof(PermissionsConfigureParams))] [JsonSerializable(typeof(PermissionsConfigureResult))] +[JsonSerializable(typeof(PermissionsEvaluateManagedPolicyParams))] +[JsonSerializable(typeof(PermissionsEvaluateManagedPolicyResult))] [JsonSerializable(typeof(PermissionsFolderTrustAddTrustedResult))] [JsonSerializable(typeof(PermissionsGetModeRequest))] [JsonSerializable(typeof(PermissionsGetModeResult))] @@ -53015,6 +57582,8 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(PermissionsSetRequiredRequest))] [JsonSerializable(typeof(PermissionsSetRequiredResult))] [JsonSerializable(typeof(PermissionsUrlsSetUnrestrictedModeResult))] +[JsonSerializable(typeof(PersistenceWriteRawJsonLockedRequest))] +[JsonSerializable(typeof(PersistenceWriteRawJsonLockedResult))] [JsonSerializable(typeof(PingRequest))] [JsonSerializable(typeof(PingResult))] [JsonSerializable(typeof(PlanReadResult))] @@ -53122,7 +57691,13 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(RemoteSessionConnectionResult))] [JsonSerializable(typeof(RemoteSessionMetadataRepository))] [JsonSerializable(typeof(RemoteSessionMetadataValue))] +[JsonSerializable(typeof(RepoSettingsEditRequest))] +[JsonSerializable(typeof(RepoSettingsReadRawRequest))] +[JsonSerializable(typeof(RepoSettingsReadRawResult))] +[JsonSerializable(typeof(RepoSettingsWriteAllRequest))] +[JsonSerializable(typeof(RepoSettingsWriteKeyRequest))] [JsonSerializable(typeof(ResponseFormat))] +[JsonSerializable(typeof(SandboxApplyDefaultsRequest))] [JsonSerializable(typeof(SandboxConfig))] [JsonSerializable(typeof(SandboxConfigAuth))] [JsonSerializable(typeof(SandboxConfigUserPolicy))] @@ -53132,18 +57707,37 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(SandboxConfigUserPolicyNetwork))] [JsonSerializable(typeof(SandboxConfigUserPolicyNetworkProxy))] [JsonSerializable(typeof(SandboxConfigUserPolicySeatbelt))] +[JsonSerializable(typeof(SandboxCredentialSuggestion))] +[JsonSerializable(typeof(SandboxCredentialSuggestionsResult))] [JsonSerializable(typeof(SandboxCredentialsConfig))] [JsonSerializable(typeof(SandboxDisableForSessionRequest))] [JsonSerializable(typeof(SandboxDisableForSessionResult))] +[JsonSerializable(typeof(SandboxDocsUrlResult))] +[JsonSerializable(typeof(SandboxEffectiveForOptionalParams))] +[JsonSerializable(typeof(SandboxEffectiveForOptionalResult))] +[JsonSerializable(typeof(SandboxEnforcementCommandAllowedParams))] [JsonSerializable(typeof(SandboxEnforcementStatus))] +[JsonSerializable(typeof(SandboxFilterPickerCandidatesParams))] +[JsonSerializable(typeof(SandboxFilterPickerCandidatesResult))] [JsonSerializable(typeof(SandboxGrantPathForRequestRequest))] [JsonSerializable(typeof(SandboxGrantPathForRequestResult))] [JsonSerializable(typeof(SandboxHostCapability))] [JsonSerializable(typeof(SandboxHostSupport))] [JsonSerializable(typeof(SandboxMaskedEnvVar))] +[JsonSerializable(typeof(SandboxPickerCandidate))] +[JsonSerializable(typeof(SandboxPreparePickerPolicyParams))] +[JsonSerializable(typeof(SandboxPreparePickerPolicyResult))] [JsonSerializable(typeof(SandboxProxyCaCreateResult))] [JsonSerializable(typeof(SandboxProxyCaRequest))] [JsonSerializable(typeof(SandboxProxyCaStatus))] +[JsonSerializable(typeof(SandboxProxySecretGetResult))] +[JsonSerializable(typeof(SandboxProxySecretLocation))] +[JsonSerializable(typeof(SandboxProxySecretSaveRequest))] +[JsonSerializable(typeof(SandboxProxySecretSaveResult))] +[JsonSerializable(typeof(SandboxResolveFlooredConfigParams))] +[JsonSerializable(typeof(SandboxResolveFlooredConfigResult))] +[JsonSerializable(typeof(SandboxUnsupportedStartupNoticeParams))] +[JsonSerializable(typeof(SandboxUnsupportedStartupNoticeResult))] [JsonSerializable(typeof(ScheduleAddAtRequest))] [JsonSerializable(typeof(ScheduleAddCronRequest))] [JsonSerializable(typeof(ScheduleAddRequest))] @@ -53297,6 +57891,9 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(SessionPluginsMarketplacesRefreshRequestWithSession))] [JsonSerializable(typeof(SessionPluginsReloadRequest))] [JsonSerializable(typeof(SessionPluginsReloadRequestWithSession))] +[JsonSerializable(typeof(SessionPluginsRetryManagedRequest))] +[JsonSerializable(typeof(SessionPluginsRetryManagedRequestWithSession))] +[JsonSerializable(typeof(SessionPluginsRetryManagedResult))] [JsonSerializable(typeof(SessionProviderGetEndpointRequest))] [JsonSerializable(typeof(SessionProviderGetEndpointRequestWithSession))] [JsonSerializable(typeof(SessionProvidersGetCatalogRequest))] @@ -53316,6 +57913,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(SessionQuotaSnapshot))] [JsonSerializable(typeof(SessionQuotaTakeWarningsRequest))] [JsonSerializable(typeof(SessionRemoteDisableRequest))] +[JsonSerializable(typeof(SessionSandboxGetCredentialSuggestionsRequest))] [JsonSerializable(typeof(SessionSandboxGetEnforcementStatusRequest))] [JsonSerializable(typeof(SessionScheduleHasSelfPacedRequest))] [JsonSerializable(typeof(SessionScheduleHydrateRequest))] @@ -53413,6 +58011,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(SessionsUpdateWorkspaceFieldsRequest))] [JsonSerializable(typeof(SessionsUpdateWorkspaceFieldsResult))] [JsonSerializable(typeof(SettableAuthInfo))] +[JsonSerializable(typeof(SettingsEditResult))] [JsonSerializable(typeof(ShellCancelUserRequestedRequest))] [JsonSerializable(typeof(ShellCredentials))] [JsonSerializable(typeof(ShellExecRequest))] @@ -53459,7 +58058,6 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(SlashCommandInput))] [JsonSerializable(typeof(SlashCommandInputChoice))] [JsonSerializable(typeof(SlashCommandInvocationResult))] -[JsonSerializable(typeof(SlashCommandInvocationResultSetModelRevertOnCancel))] [JsonSerializable(typeof(SlashCommandModelPickerDialog))] [JsonSerializable(typeof(SlashCommandSelectSubcommandOption))] [JsonSerializable(typeof(SlashCommandTimelineEntry))] @@ -53523,7 +58121,6 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(UIHandlePendingExitPlanModeRequest))] [JsonSerializable(typeof(UIHandlePendingResult))] [JsonSerializable(typeof(UIHandlePendingSamplingRequest))] -[JsonSerializable(typeof(UIHandlePendingSamplingResponse))] [JsonSerializable(typeof(UIHandlePendingSessionLimitsExhaustedRequest))] [JsonSerializable(typeof(UIHandlePendingUserInputRequest))] [JsonSerializable(typeof(UIRegisterDirectAutoModeSwitchHandlerResult))] @@ -53535,8 +58132,52 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(UpdateSubagentSettingsRequestSubagents))] [JsonSerializable(typeof(UserRequestedShellCommandResult))] [JsonSerializable(typeof(UserSettingMetadata))] +[JsonSerializable(typeof(UserSettingsApplyTopKeyRequest))] +[JsonSerializable(typeof(UserSettingsApplyTopKeyResult))] +[JsonSerializable(typeof(UserSettingsBuildRuntimeRequest))] +[JsonSerializable(typeof(UserSettingsEditRequest))] +[JsonSerializable(typeof(UserSettingsGetPathsRequest))] +[JsonSerializable(typeof(UserSettingsGetPathsResult))] [JsonSerializable(typeof(UserSettingsGetResult))] +[JsonSerializable(typeof(UserSettingsGovernanceKeysResult))] +[JsonSerializable(typeof(UserSettingsHostContext))] +[JsonSerializable(typeof(UserSettingsLoadAndMergeRepoRequest))] +[JsonSerializable(typeof(UserSettingsLoadAndMergeRepoResult))] +[JsonSerializable(typeof(UserSettingsLoadForConfigDirRequest))] +[JsonSerializable(typeof(UserSettingsLoadForEditResult))] +[JsonSerializable(typeof(UserSettingsLoadRepoRequest))] +[JsonSerializable(typeof(UserSettingsLoadRepoResult))] +[JsonSerializable(typeof(UserSettingsLoadWithWarningResult))] +[JsonSerializable(typeof(UserSettingsMergeManagedAndUserParams))] +[JsonSerializable(typeof(UserSettingsMergeManagedAndUserResult))] +[JsonSerializable(typeof(UserSettingsMergeRepoValueParams))] +[JsonSerializable(typeof(UserSettingsMergeRepoValueResult))] +[JsonSerializable(typeof(UserSettingsMigrateObsoleteOnDiskRequest))] +[JsonSerializable(typeof(UserSettingsMigrateObsoleteOnDiskResult))] +[JsonSerializable(typeof(UserSettingsMigrateStaffLogLevelOnceRequest))] +[JsonSerializable(typeof(UserSettingsMigrateStaffLogLevelOnceResult))] +[JsonSerializable(typeof(UserSettingsMigrateStaffUpdateChannelOnceResult))] +[JsonSerializable(typeof(UserSettingsNormalizeRequest))] +[JsonSerializable(typeof(UserSettingsNormalizeResult))] +[JsonSerializable(typeof(UserSettingsPathResult))] +[JsonSerializable(typeof(UserSettingsProblem))] +[JsonSerializable(typeof(UserSettingsProblemLocation))] +[JsonSerializable(typeof(UserSettingsProblemSource))] +[JsonSerializable(typeof(UserSettingsProblemsRequest))] +[JsonSerializable(typeof(UserSettingsProblemsResult))] +[JsonSerializable(typeof(UserSettingsResetStaffModelSelectionOnceRequest))] +[JsonSerializable(typeof(UserSettingsResetStaffModelSelectionOnceResult))] +[JsonSerializable(typeof(UserSettingsSetHookDisabledRequest))] +[JsonSerializable(typeof(UserSettingsSetHookDisabledResult))] [JsonSerializable(typeof(UserSettingsSetRequest))] +[JsonSerializable(typeof(UserSettingsUnknownKeysWarningRequest))] +[JsonSerializable(typeof(UserSettingsUnknownKeysWarningResult))] +[JsonSerializable(typeof(UserSettingsUpdateRequest))] +[JsonSerializable(typeof(UserSettingsUpdateResult))] +[JsonSerializable(typeof(UserSettingsValidateRequest))] +[JsonSerializable(typeof(UserSettingsValidateResult))] +[JsonSerializable(typeof(UserSettingsValidationIssue))] +[JsonSerializable(typeof(UserSettingsWriteAllRequest))] [JsonSerializable(typeof(VisibilityGetResult))] [JsonSerializable(typeof(VisibilitySetRequest))] [JsonSerializable(typeof(VisibilitySetResult))] @@ -53585,8 +58226,6 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(WorkspaceDiffResult))] [JsonSerializable(typeof(WorkspacesAddSummaryRequest))] [JsonSerializable(typeof(WorkspacesAddSummaryResult))] -[JsonSerializable(typeof(WorkspacesAddSummaryResultSummary))] -[JsonSerializable(typeof(WorkspacesAddSummaryResultWorkspace))] [JsonSerializable(typeof(WorkspacesAutopilotObjectiveExistsResult))] [JsonSerializable(typeof(WorkspacesCheckpoints))] [JsonSerializable(typeof(WorkspacesCreateDirectoryRequest))] diff --git a/dotnet/src/Generated/SessionEvents.cs b/dotnet/src/Generated/SessionEvents.cs index fb23ccb58a..190624d478 100644 --- a/dotnet/src/Generated/SessionEvents.cs +++ b/dotnet/src/Generated/SessionEvents.cs @@ -3014,7 +3014,7 @@ public sealed partial class SessionErrorData [JsonPropertyName("errorCode")] public string? ErrorCode { get; set; } - /// Category of error (e.g., "authentication", "authorization", "quota", "rate_limit", "context_limit", "query"). + /// Category of error (e.g., "authentication", "authorization", "policy", "quota", "rate_limit", "context_limit", "query"). [JsonPropertyName("errorType")] public required string ErrorType { get; set; } @@ -3027,6 +3027,16 @@ public sealed partial class SessionErrorData [JsonPropertyName("providerCallId")] public string? ProviderCallId { get; set; } + /// Sanitized provider-defined error parameter preserved from a structured upstream error envelope, when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("providerErrorParam")] + public string? ProviderErrorParam { get; set; } + + /// Provider-defined error type preserved from a structured upstream error envelope, when available (for example, `invalid_request_error`). + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("providerErrorType")] + public string? ProviderErrorType { get; set; } + /// What the user must do to recover, when the runtime knows of an action. The `message` never names a client affordance, so a client that offers one — a slash command, a settings pane, a link — renders it from this value. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("remediation")] @@ -3678,7 +3688,7 @@ public sealed partial class SessionUsageCheckpointData [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed partial class SessionQuotaObservationData { - /// The admitted provider's state observation. Admission observations never contain quantities or reset/percentage semantics. + /// The admitted provider's state observation. Admission balances and percentages remain absent; separate monthly usage may report its own amount and cycle. [JsonPropertyName("observation")] public required ProviderQuotaState Observation { get; set; } } @@ -4296,6 +4306,16 @@ public sealed partial class SessionPermissionRecoveryData [JsonPropertyName("onBlocked")] public required PermissionRecoveryOnBlocked OnBlocked { get; set; } + /// Subagent identifier that owns this recovery episode when the request was delegated through a parent permission service. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("ownerAgentId")] + public string? OwnerAgentId { get; set; } + + /// Root or child execution-session identifier that owns this recovery episode; omitted for legacy snapshots. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("ownerSessionId")] + public string? OwnerSessionId { get; set; } + /// Controlled reason for the latest episode transition. [JsonPropertyName("reason")] public required PermissionRecoveryReason Reason { get; set; } @@ -5218,11 +5238,21 @@ public sealed partial class AssistantUsageData [JsonPropertyName("rejectedPredictionTokens")] public long? RejectedPredictionTokens { get; set; } - /// Serialized (uncompressed) byte length of the request body. A content-free size signal. + /// Serialized (uncompressed) byte length of the request body, before any Content-Encoding. A content-free size signal. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("requestBodyBytes")] public long? RequestBodyBytes { get; set; } + /// Content-Encoding applied to the request body of the attempt that produced this outcome; identity when uncompressed. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("requestBodyEncoding")] + public ModelCallRequestBodyEncoding? RequestBodyEncoding { get; set; } + + /// Byte length of the request body actually sent on the wire for the attempt that produced this outcome, after any Content-Encoding. Equals requestBodyBytes when the body was not compressed. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("requestBodyWireBytes")] + public long? RequestBodyWireBytes { get; set; } + /// Per-request treatment/eligibility signal returned by the Copilot API in the `X-GitHub-Copilot-Request-TE` response header for the associated model call; `false` when the header was absent or unparseable. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("rte")] @@ -5274,10 +5304,35 @@ public sealed partial class AssistantUsageData [JsonPropertyName("websocketFallbackAfterMs")] public TimeSpan? WebsocketFallbackAfter { get; set; } + /// WebSocket close code received before the HTTP fallback. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("websocketFallbackCloseCode")] + public int? WebsocketFallbackCloseCode { get; set; } + + /// Normalized cause of the WebSocket failure that triggered the HTTP fallback; absent when no fallback occurred or the cause is unknown. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("websocketFallbackErrorKind")] + public ModelCallWebSocketFallbackErrorKind? WebsocketFallbackErrorKind { get; set; } + /// Why the call was carried by the HTTP fallback of a WebSocket-capable dispatcher; absent when no fallback occurred. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("websocketFallbackReason")] public ModelCallWebSocketFallbackReason? WebsocketFallbackReason { get; set; } + + /// Whether a WebSocket reconnect was attempted before falling back to HTTP. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("websocketFallbackReconnectAttempted")] + public bool? WebsocketFallbackReconnectAttempted { get; set; } + + /// Whether the HTTP fallback started during this model call, rather than being inherited from an earlier call. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("websocketFallbackStartedThisCall")] + public bool? WebsocketFallbackStartedThisCall { get; set; } + + /// HTTP status of the WebSocket upgrade rejection or WebSocket API error that triggered the HTTP fallback. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("websocketFallbackStatusCode")] + public int? WebsocketFallbackStatusCode { get; set; } } /// A detected loss of a previously cached prompt prefix. @@ -5532,11 +5587,21 @@ public sealed partial class ModelCallFailureData [JsonPropertyName("reasoningEffort")] public string? ReasoningEffort { get; set; } - /// Serialized (uncompressed) byte length of the failed request body. A content-free size signal. + /// Serialized (uncompressed) byte length of the failed request body, before any Content-Encoding. A content-free size signal. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("requestBodyBytes")] public long? RequestBodyBytes { get; set; } + /// Content-Encoding applied to the failed request body; identity when uncompressed. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("requestBodyEncoding")] + public ModelCallRequestBodyEncoding? RequestBodyEncoding { get; set; } + + /// Byte length of the failed request body actually sent on the wire, after any Content-Encoding. Equals requestBodyBytes when the body was not compressed. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("requestBodyWireBytes")] + public long? RequestBodyWireBytes { get; set; } + /// Content-free structural summary of the failing request. Contains only counts and shape flags (no prompt content), so it is safe for unrestricted telemetry. Populated only for client-error (4xx) failures. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("requestFingerprint")] @@ -5577,10 +5642,35 @@ public sealed partial class ModelCallFailureData [JsonPropertyName("websocketFallbackAfterMs")] public TimeSpan? WebsocketFallbackAfter { get; set; } + /// WebSocket close code received before the HTTP fallback. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("websocketFallbackCloseCode")] + public int? WebsocketFallbackCloseCode { get; set; } + + /// Normalized cause of the WebSocket failure that triggered the HTTP fallback; absent when no fallback occurred or the cause is unknown. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("websocketFallbackErrorKind")] + public ModelCallWebSocketFallbackErrorKind? WebsocketFallbackErrorKind { get; set; } + /// Why the failed call was carried by the HTTP fallback of a WebSocket-capable dispatcher; absent when no fallback occurred. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("websocketFallbackReason")] public ModelCallWebSocketFallbackReason? WebsocketFallbackReason { get; set; } + + /// Whether a WebSocket reconnect was attempted before falling back to HTTP. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("websocketFallbackReconnectAttempted")] + public bool? WebsocketFallbackReconnectAttempted { get; set; } + + /// Whether the HTTP fallback started during this model call, rather than being inherited from an earlier call. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("websocketFallbackStartedThisCall")] + public bool? WebsocketFallbackStartedThisCall { get; set; } + + /// HTTP status of the WebSocket upgrade rejection or WebSocket API error that triggered the HTTP fallback. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("websocketFallbackStatusCode")] + public int? WebsocketFallbackStatusCode { get; set; } } /// Internal telemetry result for one logical model operation after all orchestrator-owned retries settle. @@ -6556,6 +6646,16 @@ public sealed partial class PermissionRequestedData [JsonPropertyName("agentMode")] public SessionMode? AgentMode { get; set; } + /// Delegated agent that owns permission recovery when the owner session multiplexes remote agents. Absent otherwise. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("ownerAgentId")] + public string? OwnerAgentId { get; set; } + + /// Execution session that owns permission recovery for this request. Absent on historical events. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("ownerSessionId")] + public string? OwnerSessionId { get; set; } + /// Permission mode captured when evaluation began. Absent on historical events. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("permissionMode")] @@ -7295,7 +7395,7 @@ public sealed partial class SessionManagedPluginProgressData [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed partial class SessionManagedSettingsResolvedData { - /// Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers, and forced on when `failClosed` is true. + /// Whether an explicit enterprise policy restriction disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers; an unresolved policy does not force this on by itself. [JsonPropertyName("bypassPermissionsDisabled")] public required bool BypassPermissionsDisabled { get; set; } @@ -7308,7 +7408,7 @@ public sealed partial class SessionManagedSettingsResolvedData [JsonPropertyName("deviceManaged")] public required bool DeviceManaged { get; set; } - /// Whether managed policy could not be determined (e.g. a failed server fetch) and the session fell back to the fail-closed restriction. When true, restrictions such as disabling bypass-permissions are enforced even though `settings` may be absent. + /// Whether managed policy could not be determined (e.g. a failed server fetch) and unresolved-policy safeguards remain active. This does not by itself disable bypass-permissions; `bypassPermissionsDisabled` reports only an explicit policy restriction. [JsonPropertyName("failClosed")] public required bool FailClosed { get; set; } @@ -7321,6 +7421,11 @@ public sealed partial class SessionManagedSettingsResolvedData [JsonPropertyName("permissionsAllowIntersected")] public bool? PermissionsAllowIntersected { get; set; } + /// Reusable retained managed permission policy for managedSettings.permissions.evaluate, including source composition and session-local injection. Provided by live session snapshots; absent in older events. Refresh the context when managed policy changes. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("permissionsContext")] + public ManagedPermissionsContext? PermissionsContext { get; set; } + /// Whether the policy-helper managed-settings layer was present. The policy helper is the weakest channel: it fills keys no enterprise source set and can never replace one. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("policyHelperManaged")] @@ -8489,6 +8594,44 @@ public sealed partial class ProviderQuotaBudgetMetadata public required bool UsageAllowedWhenExhausted { get; set; } } +/// An authoritative monthly usage reading for the observation's provider, account, and service. +/// Nested data type for ProviderMonthlyUsage. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed partial class ProviderMonthlyUsage +{ + /// Nonnegative finite consumption reported by the service, preserving zero and fractions. Present only when state is available; never computed from tokens or balance differences. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("consumedQuantity")] + public double? ConsumedQuantity { get; set; } + + /// Start of the service-reported monthly billing cycle, in UTC RFC 3339. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("cycleStart")] + public string? CycleStart { get; set; } + + /// Service read time in UTC RFC 3339, not a ledger reconciliation watermark or confirmation of the latest inference charge. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("queriedAt")] + public string? QueriedAt { get; set; } + + /// End of the service-reported monthly billing cycle, in UTC RFC 3339. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("resetOn")] + public string? ResetOn { get; set; } + + /// Identity scope of the reading within its provider and service. + [JsonPropertyName("scope")] + public required ProviderMonthlyUsageScope Scope { get; set; } + + /// Availability of monthly consumption. Missing or unavailable usage must never be interpreted as zero. + [JsonPropertyName("state")] + public required ProviderMonthlyUsageState State { get; set; } + + /// Unit of consumedQuantity; independent of the parent observation's balance unit. + [JsonPropertyName("unit")] + public required ProviderQuotaUnit Unit { get; set; } +} + /// An account quota reading. Absence of a quantity is unknown, never zero. /// Nested data type for ProviderQuotaState. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] @@ -8542,6 +8685,11 @@ public sealed partial class ProviderQuotaState [JsonPropertyName("httpStatus")] public long? HttpStatus { get; set; } + /// Service-reported monthly consumption, independent of quota balances and per-call or session cost. Omitted when the service does not report monthly usage. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("monthlyUsage")] + public ProviderMonthlyUsage? MonthlyUsage { get; set; } + /// Whether this is a GET account reading or a pre-response admission observation. Observations are never merged across kinds. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("observationKind")] @@ -8687,6 +8835,11 @@ public sealed partial class CompactionCompleteCompactionTokensUsed [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("provider")] public ModelProviderRef? Provider { get; set; } + + /// Number of model requests represented by this aggregated compaction usage. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("requestCount")] + public long? RequestCount { get; set; } } /// Original request-level and effective conversation reasoning effort for a provider history boundary; the historical type name is retained for compatibility. @@ -8763,6 +8916,16 @@ public sealed partial class PermissionRecoveryData [JsonPropertyName("onBlocked")] public required PermissionRecoveryOnBlocked OnBlocked { get; set; } + /// Subagent identifier that owns this recovery episode when the request was delegated through a parent permission service. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("ownerAgentId")] + public string? OwnerAgentId { get; set; } + + /// Root or child execution-session identifier that owns this recovery episode; omitted for legacy snapshots. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("ownerSessionId")] + public string? OwnerSessionId { get; set; } + /// Controlled reason for the latest episode transition. [JsonPropertyName("reason")] public required PermissionRecoveryReason Reason { get; set; } @@ -13548,6 +13711,20 @@ public sealed partial class SessionLimitsExhaustedResponse public double? MaxAiCredits { get; set; } } +/// Reusable managed permission evaluation context. Treat permissions as runtime-owned policy data and retain it verbatim; it includes source-aware composition metadata. This is a snapshot, not a capability or approval token; only use contexts obtained from trusted policy sources. +/// Nested data type for ManagedPermissionsContext. +public sealed partial class ManagedPermissionsContext +{ + /// Policy could not be determined; evaluation must deny every operation. + [JsonPropertyName("failClosed")] + public required bool FailClosed { get; set; } + + /// Runtime-owned composed permissions object. Absent when no permission policy is configured. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("permissions")] + public JsonElement? Permissions { get; set; } +} + /// A single slash command available in the session, as listed by the `commands.changed` event. /// Nested data type for CommandsChangedCommand. public sealed partial class CommandsChangedCommand @@ -14311,6 +14488,9 @@ public RemediationAction(string value) /// Permit outbound network access in the sandbox policy. public static RemediationAction AllowSandboxOutbound { get; } = new("allow_sandbox_outbound"); + /// Discard conversation history back through the blocked request before continuing. + public static RemediationAction RewindConversation { get; } = new("rewind_conversation"); + /// Returns a value indicating whether two instances are equivalent. public static bool operator ==(RemediationAction left, RemediationAction right) => left.Equals(right); @@ -15884,42 +16064,42 @@ public override void Write(Utf8JsonWriter writer, ProviderQuotaCapacityState val } } -/// Timing and purpose of a provider quota observation. +/// Identity scope of a monthly usage reading. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ProviderQuotaObservationKind : IEquatable +public readonly struct ProviderMonthlyUsageScope : IEquatable { private readonly string? _value; - /// Initializes a new instance of the struct. - /// The value to associate with this . + /// Initializes a new instance of the struct. + /// The value to associate with this . [JsonConstructor] - public ProviderQuotaObservationKind(string value) + public ProviderMonthlyUsageScope(string value) { ArgumentException.ThrowIfNullOrWhiteSpace(value); _value = value; } - /// Gets the value associated with this . + /// Gets the value associated with this . public string Value => _value ?? string.Empty; - /// An explicit account read; consumption may still be subject to service aggregation delay. - public static ProviderQuotaObservationKind AccountSnapshot { get; } = new("account_snapshot"); + /// Consumption of the authenticated user within the parent observation's service and returned cycle. + public static ProviderMonthlyUsageScope User { get; } = new("user"); - /// A verdict attached before an inference response. May be cached; never a post-charge balance. - public static ProviderQuotaObservationKind AdmissionState { get; } = new("admission_state"); + /// The service did not establish a recognized usage scope. + public static ProviderMonthlyUsageScope Unknown { get; } = new("unknown"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ProviderQuotaObservationKind left, ProviderQuotaObservationKind right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ProviderMonthlyUsageScope left, ProviderMonthlyUsageScope right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ProviderQuotaObservationKind left, ProviderQuotaObservationKind right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ProviderMonthlyUsageScope left, ProviderMonthlyUsageScope right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ProviderQuotaObservationKind other && Equals(other); + public override bool Equals(object? obj) => obj is ProviderMonthlyUsageScope other && Equals(other); /// - public bool Equals(ProviderQuotaObservationKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ProviderMonthlyUsageScope other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -15927,63 +16107,66 @@ public ProviderQuotaObservationKind(string value) /// public override string ToString() => Value; - /// Provides a for serializing instances. + /// Provides a for serializing instances. [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter + public sealed class Converter : JsonConverter { /// - public override ProviderQuotaObservationKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ProviderMonthlyUsageScope Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ProviderQuotaObservationKind value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ProviderMonthlyUsageScope value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderQuotaObservationKind)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderMonthlyUsageScope)); } } } -/// Interpretation permitted for independently reported quantities. +/// Availability of a service's monthly consumption reading. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ProviderQuotaQuantityKind : IEquatable +public readonly struct ProviderMonthlyUsageState : IEquatable { private readonly string? _value; - /// Initializes a new instance of the struct. - /// The value to associate with this . + /// Initializes a new instance of the struct. + /// The value to associate with this . [JsonConstructor] - public ProviderQuotaQuantityKind(string value) + public ProviderMonthlyUsageState(string value) { ArgumentException.ThrowIfNullOrWhiteSpace(value); _value = value; } - /// Gets the value associated with this . + /// Gets the value associated with this . public string Value => _value ?? string.Empty; - /// Quantities form an authoritative available-of-entitled budget suitable for percentage presentation. - public static ProviderQuotaQuantityKind AuthoritativeBudget { get; } = new("authoritative_budget"); + /// The service reported a usable consumption reading, including an explicit zero. + public static ProviderMonthlyUsageState Available { get; } = new("available"); - /// Raw advisory quantities; do not infer permission, usage percentage, reset cadence, or post-charge balance. - public static ProviderQuotaQuantityKind AdvisoryBalance { get; } = new("advisory_balance"); + /// The service found no usage policy; no consumption amount is known. + public static ProviderMonthlyUsageState NoPolicy { get; } = new("no_policy"); - /// No quantity measurement, as with admission-only response headers. - public static ProviderQuotaQuantityKind None { get; } = new("none"); + /// The read failed or its data could not be used; consumption is unknown. + public static ProviderMonthlyUsageState Unavailable { get; } = new("unavailable"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ProviderQuotaQuantityKind left, ProviderQuotaQuantityKind right) => left.Equals(right); + /// The service reported an unrecognized usage state. + public static ProviderMonthlyUsageState Unknown { get; } = new("unknown"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ProviderQuotaQuantityKind left, ProviderQuotaQuantityKind right) => !(left == right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ProviderMonthlyUsageState left, ProviderMonthlyUsageState right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ProviderMonthlyUsageState left, ProviderMonthlyUsageState right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ProviderQuotaQuantityKind other && Equals(other); + public override bool Equals(object? obj) => obj is ProviderMonthlyUsageState other && Equals(other); /// - public bool Equals(ProviderQuotaQuantityKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ProviderMonthlyUsageState other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -15991,20 +16174,20 @@ public ProviderQuotaQuantityKind(string value) /// public override string ToString() => Value; - /// Provides a for serializing instances. + /// Provides a for serializing instances. [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter + public sealed class Converter : JsonConverter { /// - public override ProviderQuotaQuantityKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ProviderMonthlyUsageState Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ProviderQuotaQuantityKind value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ProviderMonthlyUsageState value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderQuotaQuantityKind)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderMonthlyUsageState)); } } } @@ -16076,6 +16259,131 @@ public override void Write(Utf8JsonWriter writer, ProviderQuotaUnit value, JsonS } } +/// Timing and purpose of a provider quota observation. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ProviderQuotaObservationKind : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ProviderQuotaObservationKind(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// An explicit account read; consumption may still be subject to service aggregation delay. + public static ProviderQuotaObservationKind AccountSnapshot { get; } = new("account_snapshot"); + + /// A verdict attached before an inference response. May be cached; never a post-charge balance. + public static ProviderQuotaObservationKind AdmissionState { get; } = new("admission_state"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ProviderQuotaObservationKind left, ProviderQuotaObservationKind right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ProviderQuotaObservationKind left, ProviderQuotaObservationKind right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is ProviderQuotaObservationKind other && Equals(other); + + /// + public bool Equals(ProviderQuotaObservationKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ProviderQuotaObservationKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ProviderQuotaObservationKind value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderQuotaObservationKind)); + } + } +} + +/// Interpretation permitted for independently reported quantities. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ProviderQuotaQuantityKind : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ProviderQuotaQuantityKind(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Quantities form an authoritative available-of-entitled budget suitable for percentage presentation. + public static ProviderQuotaQuantityKind AuthoritativeBudget { get; } = new("authoritative_budget"); + + /// Raw advisory quantities; do not infer permission, usage percentage, reset cadence, or post-charge balance. + public static ProviderQuotaQuantityKind AdvisoryBalance { get; } = new("advisory_balance"); + + /// No quantity measurement, as with admission-only response headers. + public static ProviderQuotaQuantityKind None { get; } = new("none"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ProviderQuotaQuantityKind left, ProviderQuotaQuantityKind right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ProviderQuotaQuantityKind left, ProviderQuotaQuantityKind right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is ProviderQuotaQuantityKind other && Equals(other); + + /// + public bool Equals(ProviderQuotaQuantityKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ProviderQuotaQuantityKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ProviderQuotaQuantityKind value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderQuotaQuantityKind)); + } + } +} + /// What initiated a conversation compaction. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] @@ -18038,6 +18346,70 @@ public override void Write(Utf8JsonWriter writer, AssistantUsageApiEndpoint valu } } +/// Content-Encoding applied to the request body sent on the wire. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ModelCallRequestBodyEncoding : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ModelCallRequestBodyEncoding(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The request body was sent uncompressed (including every WebSocket request). + public static ModelCallRequestBodyEncoding Identity { get; } = new("identity"); + + /// The request body was sent gzip-compressed. + public static ModelCallRequestBodyEncoding Gzip { get; } = new("gzip"); + + /// The request body was sent zstd-compressed. + public static ModelCallRequestBodyEncoding Zstd { get; } = new("zstd"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ModelCallRequestBodyEncoding left, ModelCallRequestBodyEncoding right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ModelCallRequestBodyEncoding left, ModelCallRequestBodyEncoding right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is ModelCallRequestBodyEncoding other && Equals(other); + + /// + public bool Equals(ModelCallRequestBodyEncoding other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ModelCallRequestBodyEncoding Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ModelCallRequestBodyEncoding value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ModelCallRequestBodyEncoding)); + } + } +} + /// Transport used for a successful model call. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] @@ -18099,6 +18471,97 @@ public override void Write(Utf8JsonWriter writer, AssistantUsageTransport value, } } +/// Normalized cause of the WebSocket failure that triggered the HTTP fallback. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ModelCallWebSocketFallbackErrorKind : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ModelCallWebSocketFallbackErrorKind(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// DNS resolution of the WebSocket host failed. + public static ModelCallWebSocketFallbackErrorKind Dns { get; } = new("dns"); + + /// The TLS handshake or a TLS record failed. + public static ModelCallWebSocketFallbackErrorKind Tls { get; } = new("tls"); + + /// A WebSocket connect or read timed out. + public static ModelCallWebSocketFallbackErrorKind Timeout { get; } = new("timeout"); + + /// The peer refused the TCP connection. + public static ModelCallWebSocketFallbackErrorKind ConnectionRefused { get; } = new("connection_refused"); + + /// The connection was reset, aborted, or ended mid-stream without a close. + public static ModelCallWebSocketFallbackErrorKind ConnectionReset { get; } = new("connection_reset"); + + /// The peer closed the WebSocket. + public static ModelCallWebSocketFallbackErrorKind ClosedByPeer { get; } = new("closed_by_peer"); + + /// The runtime closed the WebSocket itself. + public static ModelCallWebSocketFallbackErrorKind ClosedLocally { get; } = new("closed_locally"); + + /// The socket was already closed before it was used. + public static ModelCallWebSocketFallbackErrorKind NotConnected { get; } = new("not_connected"); + + /// The upgrade or request was rejected with an HTTP status. + public static ModelCallWebSocketFallbackErrorKind HttpStatus { get; } = new("http_status"); + + /// A WebSocket protocol violation or an undecodable frame. + public static ModelCallWebSocketFallbackErrorKind Protocol { get; } = new("protocol"); + + /// The WebSocket request could not be built. + public static ModelCallWebSocketFallbackErrorKind Configuration { get; } = new("configuration"); + + /// A failure that maps onto none of the other kinds. + public static ModelCallWebSocketFallbackErrorKind Other { get; } = new("other"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ModelCallWebSocketFallbackErrorKind left, ModelCallWebSocketFallbackErrorKind right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ModelCallWebSocketFallbackErrorKind left, ModelCallWebSocketFallbackErrorKind right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is ModelCallWebSocketFallbackErrorKind other && Equals(other); + + /// + public bool Equals(ModelCallWebSocketFallbackErrorKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ModelCallWebSocketFallbackErrorKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ModelCallWebSocketFallbackErrorKind value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ModelCallWebSocketFallbackErrorKind)); + } + } +} + /// Why a WebSocket-capable model call was carried by the HTTP fallback. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] @@ -23184,6 +23647,7 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(HumanResponseRecordedResponseAskUser))] [JsonSerializable(typeof(HumanResponseRecordedResponseExitPlanMode))] [JsonSerializable(typeof(HumanResponseRecordedResponseUserInput))] +[JsonSerializable(typeof(ManagedPermissionsContext))] [JsonSerializable(typeof(McpAppToolCallCompleteData))] [JsonSerializable(typeof(McpAppToolCallCompleteError))] [JsonSerializable(typeof(McpAppToolCallCompleteEvent))] @@ -23290,6 +23754,7 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(PersistedBinaryResult))] [JsonSerializable(typeof(PromptCacheBreakData))] [JsonSerializable(typeof(PromptCacheBreakEvent))] +[JsonSerializable(typeof(ProviderMonthlyUsage))] [JsonSerializable(typeof(ProviderQuotaBudgetMetadata))] [JsonSerializable(typeof(ProviderQuotaState))] [JsonSerializable(typeof(ResponsesReasoning))] diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index f4ba58fe4b..9a70de1fc3 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -3131,6 +3131,12 @@ public sealed class McpHttpServerConfig : McpServerConfig /// public sealed class CustomAgentConfig { + /// + /// Declarative hook actions keyed by event name, active only for this agent. + /// + [JsonPropertyName("hooks")] + public IDictionary>>? Hooks { get; set; } + /// /// Unique name of the custom agent. /// @@ -3276,6 +3282,21 @@ public sealed class LargeToolOutputConfig public string? OutputDirectory { get; set; } } +/// Non-persisted opt-in to image generation. +/// Experimental. This API may change or be removed in a future release. +[Experimental(Diagnostics.Experimental)] +public sealed class ImageGenerationConfig +{ + /// + /// Opt in to an authorized Copilot image model. False disables image generation. + /// Omission disables on create/cold resume and preserves resident resume state. + /// Not persisted: re-supply after runtime restart. Policy, permissions, offline + /// mode, and tool filters still apply. BYOK image generation is not supported. + /// + [JsonPropertyName("enabled")] + public bool? Enabled { get; set; } +} + /// /// Overrides the runtime's built-in tool-search behavior. /// Defers tools to keep the model's active tool set small. @@ -3630,6 +3651,7 @@ protected SessionConfigBase(SessionConfigBase? other) InfiniteSessions = other.InfiniteSessions; LargeOutput = other.LargeOutput; ToolSearch = other.ToolSearch; + ImageGeneration = other.ImageGeneration; Memory = other.Memory; McpServers = other.McpServers is not null ? (other.McpServers is Dictionary dict @@ -3684,6 +3706,8 @@ protected SessionConfigBase(SessionConfigBase? other) CanvasHandler = other.CanvasHandler; #pragma warning restore GHCP001 SkillDirectories = other.SkillDirectories is not null ? [.. other.SkillDirectories] : null; + AgentDirectories = other.AgentDirectories is not null ? [.. other.AgentDirectories] : null; + HookDirectories = other.HookDirectories is not null ? [.. other.HookDirectories] : null; PluginDirectories = other.PluginDirectories is not null ? [.. other.PluginDirectories] : null; InstructionDirectories = other.InstructionDirectories is not null ? [.. other.InstructionDirectories] : null; SessionLimits = other.SessionLimits; @@ -3788,9 +3812,9 @@ protected SessionConfigBase(SessionConfigBase? other) public bool? EnableOnDemandInstructionDiscovery { get; set; } /// - /// When , enables loading of file-based hooks from - /// .github/hooks/. This is separate from , which - /// controls SDK hook callback registration. + /// When , enables repository-origin file hooks from + /// .github/hooks/ and trusted repository-configured roots. + /// Does not control explicit or callback . /// public bool? EnableFileHooks { get; set; } @@ -4079,9 +4103,34 @@ protected SessionConfigBase(SessionConfigBase? other) /// public string? Agent { get; set; } - /// Directories to load skills from. + /// Explicit discovery-only skill roots, not general filesystem grants. + /// + /// Available without , subject to skill/managed disables. + /// Relative paths resolve once when supplied against the request's effective + /// ; retained roots do not move when it changes. + /// Null and omission are equivalent; an empty list clears only the explicit SDK contribution. + /// public IList? SkillDirectories { get; set; } + /// Explicit discovery-only agent roots, separate from . + /// + /// No general filesystem grant. Available without , + /// subject to source/managed disables. Relative paths resolve once when supplied against + /// the request's effective ; retained roots do not move. + /// Null and omission are equivalent; an empty list clears only the explicit SDK contribution. + /// + public IList? AgentDirectories { get; set; } + + /// Explicit discovery-only file-hook roots, separate from callback . + /// + /// No general filesystem grant. Available without or + /// (repository-origin hooks only), subject to hook/managed disables. + /// Relative paths resolve once when supplied against the request's effective + /// ; retained roots do not move when it changes. + /// Null and omission are equivalent; an empty list clears only the explicit SDK contribution. + /// + public IList? HookDirectories { get; set; } + /// /// Local filesystem paths to Open Plugins-format directories /// (https://open-plugins.com/) to load for this session. @@ -4093,7 +4142,13 @@ protected SessionConfigBase(SessionConfigBase? other) /// public IList? PluginDirectories { get; set; } - /// Additional directories to search for custom instruction files. + /// Explicit discovery-only instruction roots using existing SDK formats. + /// + /// No general filesystem grant. Available without , + /// subject to instruction/managed disables. Relative paths resolve once when supplied + /// against the request's effective ; retained roots do not move. + /// Null and omission are equivalent; an empty list clears only the explicit SDK contribution. + /// public IList? InstructionDirectories { get; set; } /// List of skill names to disable. @@ -4137,6 +4192,10 @@ protected SessionConfigBase(SessionConfigBase? other) /// public ToolSearchConfig? ToolSearch { get; set; } + /// Non-persisted image generation opt-in. Re-supply after runtime restart. + [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] + public ImageGenerationConfig? ImageGeneration { get; set; } + /// /// Configuration for session memory. When set, controls whether the /// session can read and write persistent memory. @@ -4320,6 +4379,7 @@ private SessionConfig(SessionConfig? other) : base(other) if (other is null) return; SessionId = other.SessionId; + Name = other.Name; Cloud = other.Cloud; RefreshCustomInstructions = other.RefreshCustomInstructions; } @@ -4327,6 +4387,15 @@ private SessionConfig(SessionConfig? other) : base(other) /// Optional session identifier; a new ID is generated if not provided. public string? SessionId { get; set; } + /// + /// Names the session at creation, as session.name.set would, and is validated the + /// same way: non-empty, no leading or trailing whitespace, at most 100 UTF-16 units, no + /// control characters, and no double quotes. A name also makes the session persist from + /// the start, so it can be listed and resumed without ever running a turn. + /// Local sessions only: cloud creation rejects it. + /// + public string? Name { get; set; } + /// /// Creates a remote session in the cloud instead of a local session. /// The optional repository is associated with the cloud session. diff --git a/dotnet/test/E2E/ClientOptionsE2ETests.cs b/dotnet/test/E2E/ClientOptionsE2ETests.cs index d15dccc457..2c3888011d 100644 --- a/dotnet/test/E2E/ClientOptionsE2ETests.cs +++ b/dotnet/test/E2E/ClientOptionsE2ETests.cs @@ -278,6 +278,7 @@ public async Task Should_Forward_Granular_Multitenancy_Fields_In_Create_Wire_Req var session = await Ctx.CreateSessionAsync(client, new SessionConfig { + Name = "named at create", SkipEmbeddingRetrieval = false, OrganizationCustomInstructions = "Follow org policy.", EnableOnDemandInstructionDiscovery = true, @@ -291,6 +292,7 @@ public async Task Should_Forward_Granular_Multitenancy_Fields_In_Create_Wire_Req using var capture = JsonDocument.Parse(await File.ReadAllTextAsync(capturePath)); var createRequest = GetCapturedRequestParams(capture.RootElement, "session.create"); + Assert.Equal("named at create", createRequest.GetProperty("name").GetString()); Assert.False(createRequest.GetProperty("skipEmbeddingRetrieval").GetBoolean()); Assert.Equal("Follow org policy.", createRequest.GetProperty("organizationCustomInstructions").GetString()); Assert.True(createRequest.GetProperty("enableOnDemandInstructionDiscovery").GetBoolean()); @@ -537,6 +539,7 @@ public async Task Should_Apply_Empty_Mode_Defaults_To_CreateSession_Wire_Request Assert.False(createRequest.GetProperty("enableSkills").GetBoolean()); Assert.True(createRequest.GetProperty("customAgentsLocalOnly").GetBoolean()); Assert.False(createRequest.TryGetProperty("organizationCustomInstructions", out _)); + Assert.False(createRequest.TryGetProperty("name", out _)); await session.DisposeAsync(); } diff --git a/dotnet/test/E2E/PendingWorkResumeE2ETests.cs b/dotnet/test/E2E/PendingWorkResumeE2ETests.cs index b2e3a19d26..f6b539fa9d 100644 --- a/dotnet/test/E2E/PendingWorkResumeE2ETests.cs +++ b/dotnet/test/E2E/PendingWorkResumeE2ETests.cs @@ -55,7 +55,7 @@ await session1.SendAsync(new MessageOptions var permissionEvent = await permissionRequested; Assert.IsType(initialRequest); - await suspendedClient.ForceStopAsync(); + await ForceStopAfterSessionLockObservedAsync(suspendedClient, sessionId); await using var resumedTcpClient = Ctx.CreateClient(options: new CopilotClientOptions { Connection = RuntimeConnection.ForUri(cliUrl, connectionToken: SharedToken) }); var session2 = await Ctx.ResumeSessionAsync(resumedTcpClient, sessionId, new ResumeSessionConfig @@ -70,6 +70,8 @@ await session1.SendAsync(new MessageOptions "resume_permission_tool") ], }); + var resumeEvent = await GetSingleResumeEventAsync(session2); + Assert.False(resumeEvent.Data.SessionWasActive); var permissionResult = await session2.Rpc.Permissions.HandlePendingPermissionRequestAsync( permissionEvent.Data.RequestId, @@ -172,6 +174,34 @@ public Task Should_Keep_Pending_External_Tool_Handleable_On_Cold_Resume_When_Con expectedSessionWasActive: false, expectedHandleResult: false); + private async Task ForceStopAfterSessionLockObservedAsync(CopilotClient suspendedClient, string sessionId) + { + await using var lockObserver = Ctx.CreateClient(options: new CopilotClientOptions + { + Connection = RuntimeConnection.ForStdio(), + }); + await lockObserver.StartAsync(); + await TestHelper.WaitForConditionAsync( + async () => + { + var result = await lockObserver.Rpc.Sessions.CheckInUseAsync([sessionId]); + return result.InUse.Contains(sessionId); + }, + timeout: PendingWorkTimeout, + timeoutMessage: $"Timed out waiting for session '{sessionId}' to acquire its lock."); + + await suspendedClient.ForceStopAsync(); + + await TestHelper.WaitForConditionAsync( + async () => + { + var result = await lockObserver.Rpc.Sessions.CheckInUseAsync([sessionId]); + return !result.InUse.Contains(sessionId); + }, + timeout: PendingWorkTimeout, + timeoutMessage: $"Timed out waiting for session '{sessionId}' to release its lock."); + } + private async Task AssertPendingExternalToolHandleableOnResumeAsync( bool disconnectOriginalClient, bool expectedSessionWasActive, @@ -213,30 +243,7 @@ await session1.SendAsync(new MessageOptions if (disconnectOriginalClient) { - await using var lockObserver = Ctx.CreateClient(options: new CopilotClientOptions - { - Connection = RuntimeConnection.ForStdio(), - }); - await lockObserver.StartAsync(); - await TestHelper.WaitForConditionAsync( - async () => - { - var result = await lockObserver.Rpc.Sessions.CheckInUseAsync([sessionId]); - return result.InUse.Contains(sessionId); - }, - timeout: PendingWorkTimeout, - timeoutMessage: $"Timed out waiting for session '{sessionId}' to acquire its lock."); - - await suspendedClient.ForceStopAsync(); - - await TestHelper.WaitForConditionAsync( - async () => - { - var result = await lockObserver.Rpc.Sessions.CheckInUseAsync([sessionId]); - return !result.InUse.Contains(sessionId); - }, - timeout: PendingWorkTimeout, - timeoutMessage: $"Timed out waiting for session '{sessionId}' to release its lock."); + await ForceStopAfterSessionLockObservedAsync(suspendedClient, sessionId); } await using var resumedClient = Ctx.CreateClient(options: new CopilotClientOptions { Connection = RuntimeConnection.ForUri(cliUrl, connectionToken: SharedToken) }); diff --git a/dotnet/test/E2E/PermissionE2ETests.cs b/dotnet/test/E2E/PermissionE2ETests.cs index 78e792c2c2..77b0e588ff 100644 --- a/dotnet/test/E2E/PermissionE2ETests.cs +++ b/dotnet/test/E2E/PermissionE2ETests.cs @@ -5,6 +5,7 @@ using GitHub.Copilot.Rpc; using GitHub.Copilot.Test.Harness; using Microsoft.Extensions.AI; +using Microsoft.Extensions.Logging; using System.Text.Json; using System.Text.Json.Serialization; using Xunit; @@ -173,23 +174,64 @@ internal static async Task AssertApproveAllPermissionHandlerAsync(CopilotSession [Fact] public async Task Should_Handle_Async_Permission_Handler() { + await Ctx.ConfigureForTestAsync("permissions", "Should_Handle_Async_Permission_Handler_Dotnet"); + + const string toolName = "async_permission_echo"; + const string expectedResult = "DOTNET_ASYNC_PERMISSION_RESULT"; var permissionRequestReceived = false; - var session = await CreateSessionAsync(new SessionConfig + var toolCalls = 0; + string? permissionToolCallId = null; + CopilotSession? session = null; + session = await CreateSessionAsync(new SessionConfig { + Tools = + [ + AIFunctionFactory.Create( + Echo, + toolName, + "Returns the result for the asynchronous permission test.", + serializerOptions: PermissionJsonContext.Default.Options), + ], + AvailableTools = [toolName], OnPermissionRequest = async (request, invocation) => { - permissionRequestReceived = true; + var customToolRequest = Assert.IsType(request); + Assert.Equal(toolName, customToolRequest.ToolName); + Assert.Equal(session!.SessionId, invocation.SessionId); + Assert.False(string.IsNullOrWhiteSpace(customToolRequest.ToolCallId)); + permissionToolCallId = customToolRequest.ToolCallId; await Task.Yield(); + permissionRequestReceived = true; return PermissionDecision.ApproveOnce(); } }); - await TestHelper.SendAndGetFinalAssistantMessageAsync(session, new MessageOptions + ToolExecutionCompleteEvent? toolCompleted = null; + using var subscription = session.On(evt => + { + if (evt.Data.ToolCallId == permissionToolCallId) + { + toolCompleted = evt; + } + }); + var message = await TestHelper.SendAndGetFinalAssistantMessageAsync(session, new MessageOptions { - Prompt = "Run 'echo test' and tell me what happens" + Prompt = "Call async_permission_echo once and reply with only its returned text." }); Assert.True(permissionRequestReceived, "Permission request should have been received"); + Assert.Equal(1, toolCalls); + Assert.NotNull(toolCompleted); + Assert.True(toolCompleted.Data.Success); + Assert.Equal(expectedResult, toolCompleted.Data.Result?.Content); + Assert.Contains(expectedResult, message.Data.Content); + + string Echo() + { + Assert.True(permissionRequestReceived, "The yielding permission handler must finish before the tool runs."); + Interlocked.Increment(ref toolCalls); + return expectedResult; + } } [Fact] @@ -330,6 +372,7 @@ public async Task Should_Receive_ToolCallId_In_Permission_Requests() [Fact] public async Task Should_Wait_For_Slow_Permission_Handler() + => await WithSdkDiagnosticsAsync(async () => { var handlerEntered = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var releaseHandler = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -388,7 +431,21 @@ void AddLifecycleEvent(string phase, string? toolCallId) releaseHandler.SetResult(); - var message = await sendTask; + AssistantMessageEvent? message; + try + { + message = await sendTask; + } + catch (TimeoutException) + { + lock (lifecycleLock) + { + Logger.LogWarning( + "Slow-permission timeout lifecycle: {Lifecycle}", + string.Join(", ", lifecycle.Select(evt => $"{evt.Phase}:{evt.ToolCallId}"))); + } + throw; + } var persistedEvents = await WaitForPersistedEventsAsync( session, events => @@ -424,7 +481,7 @@ void AddLifecycleEvent(string phase, string? toolCallId) // The tool should have actually run after permission was granted Assert.Contains("slow_handler_test", message?.Data.Content ?? string.Empty); - } + }); [Fact] public async Task Should_Handle_Concurrent_Permission_Requests_From_Parallel_Tools() diff --git a/dotnet/test/E2E/SessionE2ETests.cs b/dotnet/test/E2E/SessionE2ETests.cs index 377bb0147d..c3b9e7bdd9 100644 --- a/dotnet/test/E2E/SessionE2ETests.cs +++ b/dotnet/test/E2E/SessionE2ETests.cs @@ -478,14 +478,20 @@ public async Task Should_Receive_Session_Events() public async Task Send_Returns_Immediately_While_Events_Stream_In_Background() => await WithSdkDiagnosticsAsync(async () => { - var session = await CreateSessionAsync(new SessionConfig + // Reuse a custom-tool capture instead of depending on a native shell completing within its wait. + await Ctx.ConfigureForTestAsync( + "pending_work_resume", "should_continue_pending_external_tool_request_after_resume", replayOnly: true); + var toolStarted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var releaseTool = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + await using var session = await CreateSessionAsync(new SessionConfig { + Tools = [AIFunctionFactory.Create(BlockingTool, "resume_external_tool")], OnPermissionRequest = PermissionHandler.ApproveAll, }); var events = new ConcurrentQueue(); AssistantMessageEvent? message = null; - session.On(evt => + using var subscription = session.On(evt => { events.Enqueue(evt.Type); if (evt is AssistantMessageEvent assistantMessage) @@ -495,18 +501,35 @@ public async Task Send_Returns_Immediately_While_Events_Stream_In_Background() }); var idle = TestHelper.GetNextEventOfTypeAsync(session); - // Use a slow command so we can verify SendAsync() returns before completion - await session.SendAsync(new MessageOptions { Prompt = "Run 'sleep 2 && echo done'" }); - - // SendAsync() should return before turn completes (no session.idle yet) - Assert.DoesNotContain("session.idle", events); - - // Wait for turn to complete - await idle; + try + { + var send = session.SendAsync(new MessageOptions + { + Prompt = "Use resume_external_tool with value 'beta', then reply with the result.", + }); + Assert.Equal("beta", await toolStarted.Task.WaitAsync(TimeSpan.FromSeconds(60))); + await send.WaitAsync(TimeSpan.FromSeconds(60)); + Assert.DoesNotContain("session.idle", events); + Assert.False(idle.IsCompleted); + + releaseTool.TrySetResult("EXTERNAL_RESUMED_BETA"); + await idle; + + Assert.Contains("EXTERNAL_RESUMED_BETA", message?.Data.Content ?? string.Empty); + Assert.Contains("session.idle", events); + Assert.Contains("assistant.message", events); + } + finally + { + releaseTool.TrySetResult("EXTERNAL_RESUMED_BETA"); + } - Assert.Contains("done", message?.Data.Content ?? string.Empty); - Assert.Contains("session.idle", events); - Assert.Contains("assistant.message", events); + [Description("Blocks until the test releases the active turn")] + async Task BlockingTool([Description("Value to return")] string value, CancellationToken cancellationToken) + { + toolStarted.TrySetResult(value); + return await releaseTool.Task.WaitAsync(Timeout.InfiniteTimeSpan, cancellationToken); + } }); [Fact] diff --git a/dotnet/test/Harness/E2ETestBase.cs b/dotnet/test/Harness/E2ETestBase.cs index 679fbb9c6b..008bbc8074 100644 --- a/dotnet/test/Harness/E2ETestBase.cs +++ b/dotnet/test/Harness/E2ETestBase.cs @@ -39,7 +39,7 @@ protected E2ETestBase( // Wire logger into the shared context so all clients created via Ctx.CreateClient get it. Ctx.Logger = Logger; - Ctx.DiagnosticsEnabled = snapshotCategory is "pending_work_resume" or "session"; + Ctx.DiagnosticsEnabled = snapshotCategory is "pending_work_resume" or "session" or "permissions"; } /// Logger that forwards warnings and above to xunit test output. diff --git a/dotnet/test/Harness/E2ETestContext.cs b/dotnet/test/Harness/E2ETestContext.cs index a5b76fbcd2..f35fe7a659 100644 --- a/dotnet/test/Harness/E2ETestContext.cs +++ b/dotnet/test/Harness/E2ETestContext.cs @@ -85,7 +85,7 @@ await proxy.ConfigureAsync( await proxy.SetCopilotUserByTokenAsync(DefaultGitHubToken, new CopilotUserConfig( Login: "e2e-test-user", CopilotPlan: "individual_pro", - Endpoints: new CopilotUserEndpoints(Api: context.ProxyUrl, Telemetry: "https://localhost:1/telemetry"), + Endpoints: new CopilotUserEndpoints(Api: context.ProxyUrl, Telemetry: context.ProxyUrl), AnalyticsTrackingId: "e2e-test-tracking-id", Id: 12345)); return context; diff --git a/dotnet/test/Harness/ReplayProxy.cs b/dotnet/test/Harness/ReplayProxy.cs index 140642a2f7..6891aeae71 100644 --- a/dotnet/test/Harness/ReplayProxy.cs +++ b/dotnet/test/Harness/ReplayProxy.cs @@ -32,7 +32,7 @@ async Task StartCoreAsync() { FileName = "node", WorkingDirectory = Path.Join(FindRepoRoot(), "test", "harness"), - Arguments = "--import tsx server.ts", + Arguments = "startup.mjs", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, @@ -89,7 +89,10 @@ async Task StartCoreAsync() { if (e.Data == null) return; diagnostics.AppendError(e.Data); - Console.Error.WriteLine(e.Data); + if (!e.Data.StartsWith("[SDK proxy startup]", StringComparison.Ordinal)) + { + Console.Error.WriteLine(e.Data); + } }; try diff --git a/dotnet/test/Unit/ClientSessionLifetimeTests.CustomizationDirectories.cs b/dotnet/test/Unit/ClientSessionLifetimeTests.CustomizationDirectories.cs new file mode 100644 index 0000000000..7e7aee511f --- /dev/null +++ b/dotnet/test/Unit/ClientSessionLifetimeTests.CustomizationDirectories.cs @@ -0,0 +1,157 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +#if NET8_0_OR_GREATER +using GitHub.Copilot.Rpc; +using Xunit; + +namespace GitHub.Copilot.Test.Unit; + +public sealed partial class ClientSessionLifetimeTests +{ + [Fact] + public async Task CustomizationDirectories_Preserve_Positional_Discovery_Calls() + { + await using var server = await FakeCopilotServer.StartAsync(); + server.ResponseFactory = _ => new Dictionary + { + ["agents"] = Array.Empty(), + ["hooks"] = Array.Empty(), + ["sources"] = Array.Empty(), + ["paths"] = Array.Empty() + }; + await using var client = new CopilotClient(new CopilotClientOptions + { + Connection = RuntimeConnection.ForUri(server.Url) + }); + IList projects = ["project-root"]; + await client.StartAsync(); + await client.Rpc.Agents.DiscoverAsync(projects, true, CancellationToken.None); + await client.Rpc.Agents.GetDiscoveryPathsAsync(projects, true, CancellationToken.None); + await client.Rpc.Hooks.DiscoverAsync(projects, true, CancellationToken.None); + await client.Rpc.Instructions.DiscoverAsync(projects, true, CancellationToken.None); + await client.Rpc.Instructions.GetDiscoveryPathsAsync(projects, true, CancellationToken.None); + await client.Rpc.Skills.GetDiscoveryPathsAsync(projects, ["ignored"], true, CancellationToken.None); + foreach (var method in new[] + { + "agents.discover", "agents.getDiscoveryPaths", "hooks.discover", + "instructions.discover", "instructions.getDiscoveryPaths", "skills.getDiscoveryPaths" + }) + { + var payload = Assert.Single(server.Requests, request => request.Method == method).Params; + Assert.Equal("project-root", Assert.Single(payload.GetProperty("projectPaths").EnumerateArray()).GetString()); + foreach (var key in new[] { "agentDirectories", "hookDirectories", "instructionDirectories", "skillDirectories" }) + { + Assert.False(payload.TryGetProperty(key, out _)); + } + } + await client.Rpc.Agents.DiscoverAsync(new AgentsDiscoverRequest + { + AgentDirectories = ["explicit-agent-root"] + }); + var explicitPayload = server.Requests.Last(request => request.Method == "agents.discover").Params; + Assert.Equal("explicit-agent-root", Assert.Single(explicitPayload.GetProperty("agentDirectories").EnumerateArray()).GetString()); + Assert.False(explicitPayload.TryGetProperty("projectPaths", out _)); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + public async Task CustomizationDirectories_Are_Preserved_On_Create_And_Resume(int kind) + { + IList? roots = kind switch + { + 0 => null, + 1 => [], + _ => ["relative-root", "other-root"] + }; + var directories = new Dictionary?> + { + ["skillDirectories"] = roots?.Select(root => root + "-skills").ToList(), + ["agentDirectories"] = roots?.Select(root => root + "-agents").ToList(), + ["instructionDirectories"] = roots?.Select(root => root + "-instructions").ToList(), + ["hookDirectories"] = roots?.Select(root => root + "-hooks").ToList() + }; + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions + { + Connection = RuntimeConnection.ForUri(server.Url) + }); + await using var created = await client.CreateSessionAsync(new SessionConfig + { + WorkingDirectory = "request-cwd", + EnableConfigDiscovery = false, + EnableFileHooks = false, + SkillDirectories = directories["skillDirectories"], + AgentDirectories = directories["agentDirectories"], + InstructionDirectories = directories["instructionDirectories"], + HookDirectories = directories["hookDirectories"] + }); + await using var resumed = await client.ResumeSessionAsync("resumed", new ResumeSessionConfig + { + WorkingDirectory = "resume-cwd", + EnableConfigDiscovery = false, + EnableFileHooks = false, + SkillDirectories = directories["skillDirectories"], + AgentDirectories = directories["agentDirectories"], + InstructionDirectories = directories["instructionDirectories"], + HookDirectories = directories["hookDirectories"] + }); + foreach (var method in new[] { "session.create", "session.resume" }) + { + var payload = Assert.Single(server.Requests, request => request.Method == method).Params; + foreach (var entry in directories) + { + if (entry.Value is null) + { + Assert.False(payload.TryGetProperty(entry.Key, out _)); + } + else + { + Assert.Equal(entry.Value, payload.GetProperty(entry.Key).EnumerateArray().Select(item => item.GetString())); + } + } + Assert.False(payload.GetProperty("enableConfigDiscovery").GetBoolean()); + Assert.False(payload.GetProperty("enableFileHooks").GetBoolean()); + Assert.False(payload.TryGetProperty("additionalDirectories", out _)); + Assert.False(payload.TryGetProperty("customAgents", out _)); + Assert.False(payload.TryGetProperty("hooks", out _)); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void CustomizationDirectories_Clone_Owns_Its_Lists(bool resume) + { + SessionConfigBase original; + SessionConfigBase clone; + if (resume) + { + var config = new ResumeSessionConfig + { + AgentDirectories = ["agents"], + HookDirectories = ["hooks"] + }; + original = config; + clone = config.Clone(); + } + else + { + var config = new SessionConfig + { + AgentDirectories = ["agents"], + HookDirectories = ["hooks"] + }; + original = config; + clone = config.Clone(); + } + clone.AgentDirectories!.Add("clone-agents"); + clone.HookDirectories!.Add("clone-hooks"); + Assert.Equal("agents", Assert.Single(original.AgentDirectories!)); + Assert.Equal("hooks", Assert.Single(original.HookDirectories!)); + Assert.Collection(clone.AgentDirectories, item => Assert.Equal("agents", item), item => Assert.Equal("clone-agents", item)); + Assert.Collection(clone.HookDirectories, item => Assert.Equal("hooks", item), item => Assert.Equal("clone-hooks", item)); + } +} +#endif diff --git a/dotnet/test/Unit/ClientSessionLifetimeTests.cs b/dotnet/test/Unit/ClientSessionLifetimeTests.cs index 5beb89ae05..c3ea694395 100644 --- a/dotnet/test/Unit/ClientSessionLifetimeTests.cs +++ b/dotnet/test/Unit/ClientSessionLifetimeTests.cs @@ -24,6 +24,40 @@ public sealed partial class ClientSessionLifetimeTests { private sealed record RpcRequestRecord(string Method, JsonElement Params); + [Theory] + [InlineData(false, true)] + [InlineData(false, false)] + [InlineData(false, null)] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(true, null)] + public async Task ImageGeneration_ForwardsCreateAndResumeOptIn(bool resume, bool? enabled) + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + var imageGeneration = enabled is null ? null : new ImageGenerationConfig { Enabled = enabled }; + await using var session = resume + ? await client.ResumeSessionAsync("image-test", new ResumeSessionConfig + { + OnPermissionRequest = PermissionHandler.ApproveAll, + ImageGeneration = imageGeneration + }.Clone()) + : await client.CreateSessionAsync(new SessionConfig + { + OnPermissionRequest = PermissionHandler.ApproveAll, + ImageGeneration = imageGeneration + }.Clone()); + var request = Assert.Single(server.Requests, r => r.Method == (resume ? "session.resume" : "session.create")).Params; + if (enabled is null) + { + Assert.False(request.TryGetProperty("imageGeneration", out _), request.ToString()); + } + else + { + Assert.Equal(enabled.Value, request.GetProperty("imageGeneration").GetProperty("enabled").GetBoolean()); + } + } + [Theory] [InlineData(CopilotClientMode.CopilotCli, null, null)] [InlineData(CopilotClientMode.Empty, null, null)] diff --git a/dotnet/test/Unit/E2ETestFixtureTests.cs b/dotnet/test/Unit/E2ETestFixtureTests.cs index 933b82c1fc..afbb87fd6f 100644 --- a/dotnet/test/Unit/E2ETestFixtureTests.cs +++ b/dotnet/test/Unit/E2ETestFixtureTests.cs @@ -4,7 +4,11 @@ using System.ComponentModel; using System.Diagnostics; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; using System.Reflection; +using System.Text.Json; using GitHub.Copilot.Test.E2E; using GitHub.Copilot.Test.Harness; using Xunit; @@ -13,6 +17,25 @@ namespace GitHub.Copilot.Test.Unit; public class E2ETestFixtureTests { + [Fact] + public async Task Default_Telemetry_Endpoint_Uses_The_Owned_Proxy() + { + await using var context = await E2ETestContext.CreateAsync(); + using var client = new HttpClient(); + using var userRequest = new HttpRequestMessage(HttpMethod.Get, context.ProxyUrl + "/copilot_internal/user"); + userRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.GetEnvironment()["GH_TOKEN"]); + using var userResponse = await client.SendAsync(userRequest); + Assert.Equal(HttpStatusCode.OK, userResponse.StatusCode); + using var user = JsonDocument.Parse(await userResponse.Content.ReadAsStringAsync()); + var telemetryEndpoint = user.RootElement.GetProperty("endpoints").GetProperty("telemetry").GetString(); + Assert.Equal(context.ProxyUrl, telemetryEndpoint); + using var content = new StringContent("{}\n"); + using var telemetryResponse = await client.PostAsync(telemetryEndpoint + "/telemetry", content); + Assert.Equal(HttpStatusCode.NoContent, telemetryResponse.StatusCode); + Assert.Equal(string.Empty, await telemetryResponse.Content.ReadAsStringAsync()); + Assert.Empty(await context.GetExchangesAsync()); + } + [Fact] public async Task Failed_Process_Launch_Reclaims_Context_Directories() { diff --git a/dotnet/test/Unit/InProcessRuntimeArgumentsTests.cs b/dotnet/test/Unit/InProcessRuntimeArgumentsTests.cs new file mode 100644 index 0000000000..1dc5e4d93e --- /dev/null +++ b/dotnet/test/Unit/InProcessRuntimeArgumentsTests.cs @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +using System.Reflection; +using GitHub.Copilot.Test.Harness; +using Xunit; + +namespace GitHub.Copilot.Test.Unit; + +public sealed class InProcessRuntimeArgumentsTests +{ + [Fact] + public void Integration_Id_Is_Forwarded_To_The_Native_Host() + { + InProcessEnvIsolation.Apply("GITHUB_COPILOT_INTEGRATION_ID", " microsoft/vscode "); + var method = typeof(CopilotClient).GetMethod( + "BuildInProcessRuntimeArguments", + BindingFlags.Static | BindingFlags.NonPublic); + Assert.NotNull(method); + + var args = Assert.IsType>( + method!.Invoke(null, [new CopilotClientOptions()])); + + Assert.Contains("--cli-login-sync-integration-id=microsoft/vscode", args); + } +} diff --git a/dotnet/test/Unit/RuntimeWrapperTests.cs b/dotnet/test/Unit/RuntimeWrapperTests.cs index e7c77ab92d..47bcba2ab6 100644 --- a/dotnet/test/Unit/RuntimeWrapperTests.cs +++ b/dotnet/test/Unit/RuntimeWrapperTests.cs @@ -93,7 +93,8 @@ public async Task Marked_Bundled_Explicit_Cli_Does_Not_Require_Runtime_Pair() var nativeDirectory = Path.Combine(baseDirectory, "runtimes", rid, "native"); Directory.CreateDirectory(nativeDirectory); var cliPath = Path.Combine(nativeDirectory, OperatingSystem.IsWindows() ? "copilot.exe" : "copilot"); - await File.WriteAllTextAsync(cliPath, "not an executable"); + // Nonempty invalid images can stall Windows process creation before it reports the launch error. + await File.WriteAllTextAsync(cliPath, string.Empty); await File.WriteAllTextAsync(Path.Combine(nativeDirectory, ".copilot-explicit-cli"), "explicit"); try diff --git a/go/client.go b/go/client.go index b924a9b6c2..2d77f82956 100644 --- a/go/client.go +++ b/go/client.go @@ -975,6 +975,8 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses req.DefaultAgent = config.DefaultAgent req.Agent = config.Agent req.SkillDirectories = config.SkillDirectories + req.AgentDirectories = config.AgentDirectories + req.HookDirectories = config.HookDirectories req.PluginDirectories = config.PluginDirectories req.InstructionDirectories = config.InstructionDirectories req.DisabledSkills = config.DisabledSkills @@ -984,6 +986,7 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses req.InfiniteSessions = config.InfiniteSessions req.LargeOutput = config.LargeOutput req.ToolSearch = config.ToolSearch + req.ImageGeneration = config.ImageGeneration req.Memory = config.Memory req.GitHubToken = config.GitHubToken req.GitHubTokenProviderRegistrationID = registrationID @@ -1078,6 +1081,7 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses localSessionID = uuid.NewString() } req.SessionID = localSessionID + req.Name = config.Name // unregisterSession removes only the session created by this call and stops // its event consumer. The latter is essential on CreateSession error paths: @@ -1455,6 +1459,8 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, req.DefaultAgent = config.DefaultAgent req.Agent = config.Agent req.SkillDirectories = config.SkillDirectories + req.AgentDirectories = config.AgentDirectories + req.HookDirectories = config.HookDirectories req.PluginDirectories = config.PluginDirectories req.InstructionDirectories = config.InstructionDirectories req.DisabledSkills = config.DisabledSkills @@ -1464,6 +1470,7 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, req.InfiniteSessions = config.InfiniteSessions req.LargeOutput = config.LargeOutput req.ToolSearch = config.ToolSearch + req.ImageGeneration = config.ImageGeneration req.Memory = config.Memory req.GitHubToken = config.GitHubToken req.GitHubTokenProviderRegistrationID = registrationID @@ -2424,7 +2431,8 @@ func (c *Client) startInProcess(ctx context.Context) error { return errors.New("in-process runtime unavailable: build with the bundled embedded runtime or set COPILOT_CLI_PATH to a compatible runtime package") } - config := c.inProcessHostConfig() + integrationID := strings.TrimSpace(getEnvValue(c.options.Env, "GITHUB_COPILOT_INTEGRATION_ID")) + config := c.inProcessHostConfig(integrationID) host, err := createInProcessHost(runtimePath, cliEntrypoint, config) if err != nil { @@ -2462,7 +2470,7 @@ func (c *Client) startInProcess(ctx context.Context) error { return nil } -func (c *Client) inProcessHostConfig() inProcessHostConfig { +func (c *Client) inProcessHostConfig(cliLoginSyncIntegrationID string) inProcessHostConfig { args := make([]string, 0, 8) if c.options.LogLevel != "" { args = append(args, "--log-level", c.options.LogLevel) @@ -2485,6 +2493,9 @@ func (c *Client) inProcessHostConfig() inProcessHostConfig { if c.options.EnableRemoteSessions { args = append(args, "--remote") } + if cliLoginSyncIntegrationID != "" { + args = append(args, "--cli-login-sync-integration-id="+cliLoginSyncIntegrationID) + } environment := make(map[string]string) if c.options.GitHubToken != "" { diff --git a/go/client_test.go b/go/client_test.go index c9a6225cde..289a8e4735 100644 --- a/go/client_test.go +++ b/go/client_test.go @@ -2099,7 +2099,7 @@ func TestClient_InProcessConnection(t *testing.T) { Mode: ModeEmpty, }) - config := client.inProcessHostConfig() + config := client.inProcessHostConfig("") expectedArgs := []string{ "--log-level", "debug", "--auth-token-env", "COPILOT_SDK_AUTH_TOKEN", @@ -2119,6 +2119,33 @@ func TestClient_InProcessConnection(t *testing.T) { t.Fatalf("Expected managed environment %v, got %v", expectedEnvironment, config.Environment) } }) + + t.Run("forwards supported CLI login sync integrations", func(t *testing.T) { + t.Setenv("GITHUB_COPILOT_INTEGRATION_ID", "github/github-app") + client := NewClient(&ClientOptions{Connection: InProcessConnection{}}) + t.Setenv("GITHUB_COPILOT_INTEGRATION_ID", "changed/after-construction") + + integrationID := getEnvValue(client.options.Env, "GITHUB_COPILOT_INTEGRATION_ID") + config := client.inProcessHostConfig(integrationID) + + expectedArgs := []string{"--cli-login-sync-integration-id=github/github-app"} + if !reflect.DeepEqual(config.Args, expectedArgs) { + t.Fatalf("Expected CLI login sync arguments %v, got %v", expectedArgs, config.Args) + } + }) + + t.Run("forwards integration validation to the runtime", func(t *testing.T) { + t.Setenv("GITHUB_COPILOT_INTEGRATION_ID", "third-party/integration") + client := NewClient(&ClientOptions{Connection: InProcessConnection{}}) + + integrationID := getEnvValue(client.options.Env, "GITHUB_COPILOT_INTEGRATION_ID") + config := client.inProcessHostConfig(integrationID) + + expectedArgs := []string{"--cli-login-sync-integration-id=third-party/integration"} + if !reflect.DeepEqual(config.Args, expectedArgs) { + t.Fatalf("Expected CLI login sync arguments %v, got %v", expectedArgs, config.Args) + } + }) } func TestClient_DefaultConnection(t *testing.T) { @@ -2285,6 +2312,38 @@ func TestCreateSessionRequest_ClientName(t *testing.T) { }) } +func TestCreateSessionRequest_Name(t *testing.T) { + t.Run("includes name in JSON when set", func(t *testing.T) { + req := createSessionRequest{Name: "named at create"} + data, err := json.Marshal(req) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var m map[string]any + if err := json.Unmarshal(data, &m); err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + if m["name"] != "named at create" { + t.Errorf("Expected name to be 'named at create', got %v", m["name"]) + } + }) + + t.Run("omits name from JSON when empty", func(t *testing.T) { + req := createSessionRequest{} + data, err := json.Marshal(req) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var m map[string]any + if err := json.Unmarshal(data, &m); err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + if _, ok := m["name"]; ok { + t.Error("Expected name to be omitted when empty") + } + }) +} + func TestResumeSessionRequest_ClientName(t *testing.T) { t.Run("includes clientName in JSON when set", func(t *testing.T) { req := resumeSessionRequest{SessionID: "s1", ClientName: "my-app"} diff --git a/go/customization_directories_test.go b/go/customization_directories_test.go new file mode 100644 index 0000000000..7e7a638868 --- /dev/null +++ b/go/customization_directories_test.go @@ -0,0 +1,116 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +package copilot + +import ( + "encoding/json" + "reflect" + "testing" + + "github.com/github/copilot-sdk/go/internal/jsonrpc2" + "github.com/github/copilot-sdk/go/rpc" +) + +func TestClient_CustomizationDirectoryAdapters(t *testing.T) { + for _, tt := range []struct { + name string + roots []string + }{ + {"omitted", nil}, + {"empty", []string{}}, + {"relative", []string{"relative-root", "other-root"}}, + } { + t.Run(tt.name, func(t *testing.T) { + directories := make(map[string][]string) + for key, suffix := range map[string]string{ + "skillDirectories": "skills", + "agentDirectories": "agents", + "instructionDirectories": "instructions", + "hookDirectories": "hooks", + } { + if tt.roots != nil { + directories[key] = make([]string, 0, len(tt.roots)) + for _, root := range tt.roots { + directories[key] = append(directories[key], root+"-"+suffix) + } + } + } + rpcClient, server, _ := newRuntimeShutdownRpcPair(t) + t.Cleanup(server.Stop) + t.Cleanup(rpcClient.Stop) + client := &Client{ + client: rpcClient, + RPC: rpc.NewServerRPC(rpcClient), + sessions: make(map[string]*Session), + } + requests := make(chan json.RawMessage, 2) + for _, method := range []string{"session.create", "session.resume"} { + server.SetRequestHandler(method, func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + requests <- append(json.RawMessage(nil), params...) + id := sessionIDFromParams(t, params) + response, err := json.Marshal(map[string]string{"sessionId": id}) + if err != nil { + return nil, &jsonrpc2.Error{Code: -32603, Message: err.Error()} + } + return response, nil + }) + } + _, err := client.CreateSession(t.Context(), &SessionConfig{ + WorkingDirectory: "request-cwd", + EnableConfigDiscovery: Bool(false), + EnableFileHooks: Bool(false), + SkillDirectories: directories["skillDirectories"], + AgentDirectories: directories["agentDirectories"], + InstructionDirectories: directories["instructionDirectories"], + HookDirectories: directories["hookDirectories"], + }) + if err != nil { + t.Fatal(err) + } + _, err = client.ResumeSessionWithOptions(t.Context(), "resumed", &ResumeSessionConfig{ + WorkingDirectory: "resume-cwd", + EnableConfigDiscovery: Bool(false), + EnableFileHooks: Bool(false), + SkillDirectories: directories["skillDirectories"], + AgentDirectories: directories["agentDirectories"], + InstructionDirectories: directories["instructionDirectories"], + HookDirectories: directories["hookDirectories"], + }) + if err != nil { + t.Fatal(err) + } + for range 2 { + var payload map[string]json.RawMessage + if err := json.Unmarshal(<-requests, &payload); err != nil { + t.Fatal(err) + } + for _, key := range []string{"skillDirectories", "agentDirectories", "instructionDirectories", "hookDirectories"} { + value, present := payload[key] + if tt.roots == nil { + if present { + t.Errorf("unexpected %s: %s", key, value) + } + } else { + var actual []string + if err := json.Unmarshal(value, &actual); err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(actual, directories[key]) { + t.Errorf("%s = %#v, want %#v", key, actual, directories[key]) + } + } + } + for _, key := range []string{"enableConfigDiscovery", "enableFileHooks"} { + if string(payload[key]) != "false" { + t.Errorf("%s = %s, want false", key, payload[key]) + } + } + for _, key := range []string{"additionalDirectories", "customAgents", "hooks"} { + if _, present := payload[key]; present { + t.Errorf("unexpected %s in %s", key, payload[key]) + } + } + } + }) + } +} diff --git a/go/image_generation_test.go b/go/image_generation_test.go new file mode 100644 index 0000000000..1c6586692c --- /dev/null +++ b/go/image_generation_test.go @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +package copilot + +import ( + "encoding/json" + "testing" +) + +func TestImageGenerationSerialization(t *testing.T) { + for _, enabled := range []*bool{Bool(true), Bool(false), nil} { + var config *ImageGenerationConfig + if enabled != nil { + config = &ImageGenerationConfig{Enabled: enabled} + } + for _, request := range []any{ + createSessionRequest{ImageGeneration: config}, + resumeSessionRequest{SessionID: "image-test", ImageGeneration: config}, + } { + data, err := json.Marshal(request) + if err != nil { + t.Fatal(err) + } + var payload map[string]json.RawMessage + if err := json.Unmarshal(data, &payload); err != nil { + t.Fatal(err) + } + value, present := payload["imageGeneration"] + if enabled == nil { + if present { + t.Fatalf("expected omitted imageGeneration, got %s", data) + } + } else { + expected, err := json.Marshal(map[string]bool{"enabled": *enabled}) + if err != nil { + t.Fatal(err) + } + if string(value) != string(expected) { + t.Fatalf("imageGeneration = %s, want %s", value, expected) + } + } + } + } + data, err := json.Marshal(ImageGenerationConfig{}) + if err != nil || string(data) != "{}" { + t.Fatalf("unset enabled = %s, err = %v", data, err) + } +} diff --git a/go/internal/e2e/client_options_e2e_test.go b/go/internal/e2e/client_options_e2e_test.go index 739f1a58da..49294ca052 100644 --- a/go/internal/e2e/client_options_e2e_test.go +++ b/go/internal/e2e/client_options_e2e_test.go @@ -412,6 +412,7 @@ func TestClientOptionsE2E(t *testing.T) { extensionSDKPath := filepath.Join(ctx.WorkDir, "extension-sdk") session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ SessionID: sessionID, + Name: "go named at create", ClientName: "go-sdk-e2e-client", Model: "claude-sonnet-5", ReasoningEffort: "low", @@ -471,6 +472,7 @@ func TestClientOptionsE2E(t *testing.T) { } expectedValues := map[string]any{ "sessionId": sessionID, + "name": "go named at create", "clientName": "go-sdk-e2e-client", "model": "claude-sonnet-5", "reasoningEffort": "low", diff --git a/go/internal/jsonrpc2/jsonrpc2.go b/go/internal/jsonrpc2/jsonrpc2.go index d4898b6a9c..14566f7bea 100644 --- a/go/internal/jsonrpc2/jsonrpc2.go +++ b/go/internal/jsonrpc2/jsonrpc2.go @@ -329,11 +329,11 @@ func (c *Client) RequestWithInlineResponse(ctx context.Context, method string, p return response.Result, nil case <-c.processDone: if err := c.getProcessError(); err != nil { - return nil, err + return responseAfterShutdown(responseChan, err) } - return nil, fmt.Errorf("process exited unexpectedly") + return responseAfterShutdown(responseChan, fmt.Errorf("process exited unexpectedly")) case <-c.stopChan: - return nil, fmt.Errorf("client stopped") + return responseAfterShutdown(responseChan, fmt.Errorf("client stopped")) } } select { @@ -345,9 +345,22 @@ func (c *Client) RequestWithInlineResponse(ctx context.Context, method string, p } return response.Result, nil case <-c.connectionClosed: - return nil, fmt.Errorf("connection closed") + return responseAfterShutdown(responseChan, fmt.Errorf("connection closed")) case <-c.stopChan: - return nil, fmt.Errorf("client stopped") + return responseAfterShutdown(responseChan, fmt.Errorf("client stopped")) + } +} + +func responseAfterShutdown(responseChan <-chan *Response, shutdownErr error) (json.RawMessage, error) { + // Shutdown can win the outer select after the read loop has already admitted a reply. + select { + case response := <-responseChan: + if response.Error != nil { + return nil, response.Error + } + return response.Result, nil + default: + return nil, shutdownErr } } diff --git a/go/internal/jsonrpc2/jsonrpc2_test.go b/go/internal/jsonrpc2/jsonrpc2_test.go index 258992e101..0c42f185aa 100644 --- a/go/internal/jsonrpc2/jsonrpc2_test.go +++ b/go/internal/jsonrpc2/jsonrpc2_test.go @@ -3,7 +3,9 @@ package jsonrpc2 import ( "bytes" "context" + "encoding/json" "errors" + "fmt" "io" "sync" "testing" @@ -16,6 +18,10 @@ type writeCloser struct { func (w writeCloser) Close() error { return nil } +type writeFunc func([]byte) (int, error) + +func (w writeFunc) Write(data []byte) (int, error) { return w(data) } + func TestOnCloseCalledOnUnexpectedExit(t *testing.T) { stdinR, stdinW := io.Pipe() stdoutR, stdoutW := io.Pipe() @@ -110,6 +116,135 @@ func TestRequestReturnsWhenConnectionCloses(t *testing.T) { } } +func TestRequestUnacknowledgedShutdown(t *testing.T) { + for _, shutdown := range []string{"stop", "connection closed", "process exited", "process error"} { + t.Run(shutdown, func(t *testing.T) { + stdinR, stdinW := io.Pipe() + defer stdinR.Close() + defer stdinW.Close() + client := NewClient(stdinW, io.NopCloser(bytes.NewReader(nil))) + done := make(chan struct{}) + var processErr error + if shutdown == "process error" { + processErr = errors.New("CLI process exited: exit status 1") + } + if shutdown == "process exited" || shutdown == "process error" { + client.SetProcessDone(done, &processErr) + } + ctx, cancel := context.WithCancel(t.Context()) + finished := make(chan struct{}) + t.Cleanup(func() { + cancel() + stdinR.Close() + select { + case <-finished: + case <-time.After(time.Second): + t.Error("request did not finish during cleanup") + } + }) + result := make(chan error, 1) + go func() { + defer close(finished) + _, err := client.Request(ctx, "test.method", nil) + result <- err + }() + if _, err := newHeaderReader(stdinR).Read(); err != nil { + t.Fatalf("Read request failed: %v", err) + } + var expected string + switch shutdown { + case "stop": + close(client.stopChan) + expected = "client stopped" + case "connection closed": + close(client.connectionClosed) + expected = "connection closed" + case "process exited": + close(done) + expected = "process exited unexpectedly" + case "process error": + close(done) + expected = processErr.Error() + } + select { + case err := <-result: + if err == nil || err.Error() != expected { + t.Fatalf("unacknowledged request error = %v, want %s", err, expected) + } + case <-time.After(time.Second): + t.Fatal("unacknowledged request did not stop") + } + client.mu.Lock() + pending, callbacks := len(client.pendingRequests), len(client.pendingInlineCallbacks) + client.mu.Unlock() + if pending != 0 || callbacks != 0 { + t.Fatalf("request cleanup left %d pending requests and %d callbacks", pending, callbacks) + } + }) + } +} + +func TestRequestAdmittedResponseSurvivesShutdown(t *testing.T) { + for _, shutdown := range []string{"stop", "connection closed", "process exited", "process error"} { + t.Run(shutdown, func(t *testing.T) { + for _, rpcError := range []bool{false, true} { + for iteration := range 32 { + t.Run(fmt.Sprintf("RPC_error=%t/%d", rpcError, iteration), func(t *testing.T) { + expected := json.RawMessage(`{"messageId":"acknowledged-message"}`) + var expectedErr *Error + if rpcError { + expectedErr = &Error{Code: -32001, Message: "acknowledged failure", Data: json.RawMessage(`{"detail":"original"}`)} + } + done := make(chan struct{}) + var client *Client + writer := writeFunc(func(data []byte) (int, error) { + if bytes.HasPrefix(data, []byte("Content-Length:")) { + return len(data), nil + } + var request Request + if err := json.Unmarshal(data, &request); err != nil { + return 0, err + } + // Both outcomes are ready before Request reaches its response select. + client.handleResponse(&Response{ID: request.ID, Result: expected, Error: expectedErr}) + switch shutdown { + case "stop": + close(client.stopChan) + case "connection closed": + close(client.connectionClosed) + default: + close(done) + } + return len(data), nil + }) + client = NewClient(writeCloser{Writer: writer}, io.NopCloser(bytes.NewReader(nil))) + if shutdown == "process exited" || shutdown == "process error" { + var processErr error + if shutdown == "process error" { + processErr = errors.New("CLI process exited: exit status 1") + } + client.SetProcessDone(done, &processErr) + } + + result, err := client.Request(t.Context(), "test.method", nil) + + if expectedErr != nil { + if result != nil || err != expectedErr { + t.Fatalf("admitted error = (%s, %v), want original RPC error %v", result, err, expectedErr) + } + } else if err != nil || !bytes.Equal(result, expected) { + t.Fatalf("admitted response = (%s, %v), want (%s, nil)", result, err, expected) + } + if len(client.pendingRequests) != 0 || len(client.pendingInlineCallbacks) != 0 { + t.Fatal("completed request retained its pending response or callback") + } + }) + } + } + }) + } +} + // TestSetProcessDone_ErrorAvailableImmediately validates that getProcessError() // returns the correct error immediately after processDone is closed. // The current implementation stores a pointer to the process error diff --git a/go/rpc/repo_settings_test.go b/go/rpc/repo_settings_test.go new file mode 100644 index 0000000000..529daa40ff --- /dev/null +++ b/go/rpc/repo_settings_test.go @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +package rpc_test + +import ( + "encoding/json" + "fmt" + "reflect" + "testing" + + "github.com/github/copilot-sdk/go/rpc" +) + +func ExampleRepoSettingsReadRawResult() { + const input = `{"config":{"custom":{"items":[true,null,12.5]}},"invalid":false}` + var result rpc.RepoSettingsReadRawResult + if err := json.Unmarshal([]byte(input), &result); err != nil { + panic(err) + } + output, err := json.Marshal(result) + if err != nil { + panic(err) + } + fmt.Println(string(output)) + // Output: {"config":{"custom":{"items":[true,null,12.5]}},"invalid":false} +} + +func TestRawRepoSettingsPreserveUnknownJSON(t *testing.T) { + for _, input := range []string{ + `{"config":{"custom":{"items":[true,null,12.5],"text":"preserved"}},"invalid":false}`, + `{"config":{},"invalid":true}`, + } { + t.Run(input, func(t *testing.T) { + var decoded rpc.RepoSettingsReadRawResult + if err := json.Unmarshal([]byte(input), &decoded); err != nil { + t.Fatal(err) + } + output, err := json.Marshal(decoded) + if err != nil { + t.Fatal(err) + } + var expected, actual any + if err := json.Unmarshal([]byte(input), &expected); err != nil { + t.Fatal(err) + } + if err := json.Unmarshal(output, &actual); err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("repository settings changed:\nactual: %s\nexpected: %s", output, input) + } + }) + } +} diff --git a/go/rpc/settings_presence_test.go b/go/rpc/settings_presence_test.go new file mode 100644 index 0000000000..86c562e194 --- /dev/null +++ b/go/rpc/settings_presence_test.go @@ -0,0 +1,107 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +package rpc + +import ( + "context" + "encoding/json" + "net" + "testing" + "time" + + "github.com/github/copilot-sdk/go/internal/jsonrpc2" +) + +func TestSettingsValuePresenceOnWire(t *testing.T) { + for _, method := range []string{"user.settings.applyTopKey", "repo.settings.writeKey", "user.settings.edit"} { + for _, test := range []struct { + name string + value any + present bool + delete bool + }{ + {name: "omitted"}, + {name: "explicit null", value: json.RawMessage("null"), present: true}, + {name: "value", value: "chosen", present: true}, + {name: "delete", delete: true}, + } { + t.Run(method+"/"+test.name, func(t *testing.T) { + clientConn, serverConn := net.Pipe() + defer clientConn.Close() + defer serverConn.Close() + client := jsonrpc2.NewClient(clientConn, clientConn) + server := jsonrpc2.NewClient(serverConn, serverConn) + requests := make(chan json.RawMessage, 1) + server.SetRequestHandler(method, func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + requests <- params + return json.RawMessage(`{}`), nil + }) + client.Start() + server.Start() + defer client.Stop() + defer server.Stop() + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + api := NewInternalServerRPC(client) + var err error + switch method { + case "user.settings.applyTopKey": + var value *any + if test.present { + value = &test.value + } + _, err = api.User.Settings().ApplyTopKey(ctx, &UserSettingsApplyTopKeyRequest{ + CanonicalPath: []string{"model"}, + TopKey: "model", + Value: value, + Delete: &test.delete, + }) + case "repo.settings.writeKey": + _, err = api.Repo.Settings().WriteKey(ctx, &RepoSettingsWriteKeyRequest{ + RepoRoot: t.TempDir(), + Scope: "repo", + Key: "model", + Value: test.value, + Delete: &test.delete, + }) + case "user.settings.edit": + operation := SettingsEditOperationSet + if test.delete { + operation = SettingsEditOperationRemove + } + _, err = api.User.Settings().Edit(ctx, &UserSettingsEditRequest{ + CanonicalPath: []string{"model"}, + Operation: operation, + Value: test.value, + }) + } + if err != nil { + t.Fatal(err) + } + select { + case raw := <-requests: + var fields map[string]json.RawMessage + if err := json.Unmarshal(raw, &fields); err != nil { + t.Fatal(err) + } + value, present := fields["value"] + wantPresent := test.present || (method == "user.settings.edit" && !test.delete) + if present != wantPresent { + t.Fatalf("value presence = %v, want %v; wire params: %s", present, wantPresent, raw) + } + if present { + expected, err := json.Marshal(test.value) + if err != nil { + t.Fatal(err) + } + if string(value) != string(expected) { + t.Fatalf("value = %s, want %s; wire params: %s", value, expected, raw) + } + } + case <-ctx.Done(): + t.Fatal(ctx.Err()) + } + }) + } + } +} diff --git a/go/rpc/zrpc.go b/go/rpc/zrpc.go index 9ac9b92597..cc7b86d136 100644 --- a/go/rpc/zrpc.go +++ b/go/rpc/zrpc.go @@ -39,6 +39,28 @@ type AccountAllUsers struct { Token *string `json:"token,omitempty"` } +// The authentication identity whose shared wording should be rendered. +// Experimental: AccountDescribeAuthInfoRequest is part of an experimental API and may +// change or be removed. +// Internal: AccountDescribeAuthInfoRequest is an internal SDK API and is not part of the +// public surface. +type AccountDescribeAuthInfoRequest struct { + // Identity to describe. + Identity AuthIdentity `json:"identity"` +} + +// The wording the runtime uses for an authentication identity. +// Experimental: AccountDescribeAuthInfoResult is part of an experimental API and may change +// or be removed. +// Internal: AccountDescribeAuthInfoResult is an internal SDK API and is not part of the +// public surface. +type AccountDescribeAuthInfoResult struct { + // One-line description of the identity, suitable after a verb such as "Switched to". + Description string `json:"description"` + // Sign-in banner announcing the authenticated user. + Message string `json:"message"` +} + // List of all authenticated users // Experimental: AccountGetAllUsersResult is part of an experimental API and may change or // be removed. @@ -54,6 +76,30 @@ type AccountGetCurrentAuthResult struct { AuthInfo AuthInfo `json:"authInfo,omitempty"` } +// The authentication identity whose gist support should be reported. +// Experimental: AccountGetGistSupportRequest is part of an experimental API and may change +// or be removed. +// Internal: AccountGetGistSupportRequest is an internal SDK API and is not part of the +// public surface. +type AccountGetGistSupportRequest struct { + // Identity to evaluate. + Identity AuthIdentity `json:"identity"` +} + +// Whether the identity can create gists, and why not when it cannot. +// Experimental: AccountGetGistSupportResult is part of an experimental API and may change +// or be removed. +// Internal: AccountGetGistSupportResult is an internal SDK API and is not part of the +// public surface. +type AccountGetGistSupportResult struct { + // Machine-readable reason category when unsupported. + Kind *string `json:"kind,omitempty"` + // Human-readable explanation to show where the gist action would have been. + Reason *string `json:"reason,omitempty"` + // True when the identity can create gists. + Supported bool `json:"supported"` +} + // Experimental: AccountGetQuotaRequest is part of an experimental API and may change or be // removed. type AccountGetQuotaRequest struct { @@ -73,6 +119,40 @@ type AccountGetQuotaResult struct { QuotaSnapshots map[string]AccountQuotaSnapshot `json:"quotaSnapshots"` } +// The environment variable supplying a classic personal access token, when one is set. +// Experimental: AccountGetTokenEnvVarResult is part of an experimental API and may change +// or be removed. +// Internal: AccountGetTokenEnvVarResult is an internal SDK API and is not part of the +// public surface. +type AccountGetTokenEnvVarResult struct { + // Variable name, for example `GH_TOKEN`. Absent when no classic token is present in the + // environment. + Name *string `json:"name,omitempty"` +} + +// A path to resolve against the GitHub host an identity is signed in to. +// Experimental: AccountGetWebURLRequest is part of an experimental API and may change or be +// removed. +// Internal: AccountGetWebURLRequest is an internal SDK API and is not part of the public +// surface. +type AccountGetWebURLRequest struct { + // Identity whose host the path resolves against. Omit to resolve against the public + // github.com host. + Identity *AuthIdentity `json:"identity,omitempty"` + // Host-relative path, without a leading slash, for example `settings/copilot/features`. + Path string `json:"path"` +} + +// An absolute URL on the identity's GitHub host. +// Experimental: AccountGetWebURLResult is part of an experimental API and may change or be +// removed. +// Internal: AccountGetWebURLResult is an internal SDK API and is not part of the public +// surface. +type AccountGetWebURLResult struct { + // Absolute URL for the requested path. + URL string `json:"url"` +} + // Credentials to validate and store. Omit login to resolve the authenticated user from the // token. // Experimental: AccountLoginRequest is part of an experimental API and may change or be @@ -167,6 +247,9 @@ type AccountsSetRequest struct { type AccountStatus struct { // Whether this is the active account. Active bool `json:"active"` + // Human-readable credential source used to authenticate this account, such as `gh` or + // `GITHUB_TOKEN`. + AuthSource *string `json:"authSource,omitempty"` // Opaque id of the account this one was derived from (e.g. an EMU account's base Entra // identity); absent for a root account. Matches the base identity account's selectionId, // forming the derivation edge. @@ -223,6 +306,8 @@ type AgentInfo struct { DisableModelInvocation *bool `json:"disableModelInvocation,omitempty"` // Human-readable display name DisplayName string `json:"displayName"` + // Declarative hook actions keyed by event name, executed only while this agent is active. + Hooks map[string][]any `json:"hooks,omitzero"` // Stable identifier for selection. For most agents this is the same as `name`; for // plugin/builtin agents it may differ. Always populated; defaults to `name` when no // distinct id was assigned. @@ -503,6 +588,11 @@ type AgentsCustomAgentInitialModelDecisionResult struct { // Experimental: AgentsDiscoverRequest is part of an experimental API and may change or be // removed. type AgentsDiscoverRequest struct { + // Explicit discovery-only agent roots, separate from customAgents and filesystem grants. + // Available without ambient discovery, subject to managed/source disables. Relative paths + // resolve against the request's effective working directory when supplied. Null and + // omission are equivalent; an empty array contributes no explicit roots. + AgentDirectories []string `json:"agentDirectories,omitzero"` // When true, omit the host's agents (the user-level agent directory and all plugin agents), // leaving only project and remote agents. For multitenant deployments. ExcludeHostAgents *bool `json:"excludeHostAgents,omitempty"` @@ -636,6 +726,11 @@ type AgentsGetBuiltinsResult struct { // Experimental: AgentsGetDiscoveryPathsRequest is part of an experimental API and may // change or be removed. type AgentsGetDiscoveryPathsRequest struct { + // Explicit discovery-only agent roots to enumerate, including configured missing + // directories. Relative paths resolve against the request's effective working directory + // when supplied. These are not filesystem grants or preferred creation targets. Null and + // omission are equivalent. + AgentDirectories []string `json:"agentDirectories,omitzero"` // When true, omit the host's user-level agent directory, leaving only project directories. // For multitenant deployments (mirrors `discover`'s `excludeHostAgents`). ExcludeHostAgents *bool `json:"excludeHostAgents,omitempty"` @@ -4951,6 +5046,126 @@ type ExternalToolTextResultForLlmContentResourceLinkIcon struct { Theme *ExternalToolTextResultForLlmContentResourceLinkIconTheme `json:"theme,omitempty"` } +// The declared shape of one feature flag, independent of any caller population. +// Experimental: FeatureFlagMetadata is part of an experimental API and may change or be +// removed. +// Internal: FeatureFlagMetadata is an internal SDK API and is not part of the public +// surface. +type FeatureFlagMetadata struct { + // The population the flag is declared for before any environment or user-settings override + // is applied. + // Internal: Availability is part of the SDK's internal API surface and is not intended for + // external use. + Availability FeatureFlagAvailability `json:"availability"` + // Whether the flag participates in the CAPI sanity suite. + CapiSanity bool `json:"capiSanity"` + // Text shown to a user choosing whether to turn the flag on in experimental mode. Absent + // when the flag is not user-facing. + ExperimentalDescription *string `json:"experimentalDescription,omitempty"` + // The online-experiment (ExP) flag this maps to, when the flag is assigned by an experiment + // rather than declared statically. + ExpFlag *string `json:"expFlag,omitempty"` + // Environment variable that overrides this flag's experiment assignment locally in the + // experiment service, not in `featureFlags.resolve`. Present exactly when `expFlag` is, and + // derived from it, so a caller reads a local override without reproducing the naming rule. + ExpFlagEnvVar *string `json:"expFlagEnvVar,omitempty"` + // The flag's name, matching the key it is listed under. + Name string `json:"name"` +} + +// Every feature flag this runtime declares, with the shape each one was declared with. +// Experimental: FeatureFlagsMetadataResult is part of an experimental API and may change or +// be removed. +// Internal: FeatureFlagsMetadataResult is an internal SDK API and is not part of the public +// surface. +type FeatureFlagsMetadataResult struct { + // Declared feature flags keyed by flag name. + // Internal: Flags is part of the SDK's internal API surface and is not intended for + // external use. + Flags map[string]FeatureFlagMetadata `json:"flags"` +} + +// Caller population and overrides for static feature-flag resolution, independent of +// experiment assignments and active runtime state. +// Experimental: FeatureFlagsResolveRequest is part of an experimental API and may change or +// be removed. +// Internal: FeatureFlagsResolveRequest is an internal SDK API and is not part of the public +// surface. +type FeatureFlagsResolveRequest struct { + // Resolve against this already-computed flag set rather than each flag's declared + // availability. Keyed by flag name. + BaseFlags map[string]bool `json:"baseFlags,omitzero"` + // User settings to apply, including `enabledFeatureFlags`, legacy `feature_flags.enabled`, + // and supported convenience settings. + Config any `json:"config,omitempty"` + // Environment for direct feature-flag overrides and `COPILOT_CLI_ENABLED_FEATURE_FLAGS`. + // Does not apply `COPILOT_EXP_*` experiment assignments. Defaults to this runtime process's + // own environment. + Environment map[string]string `json:"environment,omitzero"` + // Flags forced to a value after every other layer, keyed by flag name. + FlagOverrides map[string]bool `json:"flagOverrides,omitzero"` + // Whether the caller is in an experimental population. + IsExperimental bool `json:"isExperimental"` + // Whether the caller is GitHub or Microsoft staff. + IsStaff bool `json:"isStaff"` + // GitHub-staff membership. Omit when it was never recorded: a legacy staff account then + // counts as both staff populations and keeps every staff feature. + IsStaffGitHub *bool `json:"isStaffGithub,omitempty"` + // Microsoft-staff membership. Omitted the same way as `isStaffGitHub`. + IsStaffMicrosoft *bool `json:"isStaffMicrosoft,omitempty"` + // Whether the caller belongs to the team population used by feature availability. + IsTeam bool `json:"isTeam"` +} + +// The resolved feature flags, keyed by flag name. +// Experimental: FeatureFlagsResolveResult is part of an experimental API and may change or +// be removed. +type FeatureFlagsResolveResult map[string]bool + +// Caller population and startup inputs to resolve named online-experiment gates for. +// Carries no account: the runtime resolves the assignment for the authentication it already +// holds. +// Experimental: FeatureFlagsResolveStartupGatesRequest is part of an experimental API and +// may change or be removed. +// Internal: FeatureFlagsResolveStartupGatesRequest is an internal SDK API and is not part +// of the public surface. +type FeatureFlagsResolveStartupGatesRequest struct { + // User settings document whose `featureFlags` block is layered on top of the resolved set. + Config any `json:"config,omitempty"` + // Environment to read flag overrides from. Defaults to this runtime process's own + // environment. + Environment map[string]string `json:"environment,omitzero"` + // When this installation first launched, in milliseconds since the Unix epoch. Experiments + // that segment on installation age read it. Omit when the first launch was never recorded. + FirstLaunchAtMillis *float64 `json:"firstLaunchAtMillis,omitempty"` + // The feature flags to resolve. A name this runtime does not declare is reported as an + // error rather than as a disabled flag. + Flags []string `json:"flags"` + // Whether the caller is in an experimental population. + IsExperimental bool `json:"isExperimental"` + // Whether the caller is GitHub or Microsoft staff. + IsStaff bool `json:"isStaff"` + // GitHub-staff membership. Omit when it was never recorded: a legacy staff account then + // counts as both staff populations and keeps every staff feature. + IsStaffGitHub *bool `json:"isStaffGithub,omitempty"` + // Microsoft-staff membership. Omitted the same way as `isStaffGitHub`. + IsStaffMicrosoft *bool `json:"isStaffMicrosoft,omitempty"` + // Whether the caller belongs to a team plan. + IsTeam bool `json:"isTeam"` + // Whether the runtime may refresh the cached assignment over the network without an active + // session. The refresh never blocks this call: the answer comes from the cache either way, + // so a refresh only affects a later launch. + RefreshWithoutSession bool `json:"refreshWithoutSession"` + // Whether streamer mode is on, which withholds the populations an experiment would + // otherwise segment on. + StreamerMode bool `json:"streamerMode"` +} + +// The resolved gates, keyed by flag name. +// Experimental: FeatureFlagsResolveStartupGatesResult is part of an experimental API and +// may change or be removed. +type FeatureFlagsResolveStartupGatesResult map[string]bool + // Content filtering mode to apply to all tools, or a map of tool name to content filtering // mode. // Experimental: FilterMapping is part of an experimental API and may change or be removed. @@ -5016,6 +5231,59 @@ type FolderTrustCheckResult struct { Trusted bool `json:"trusted"` } +// A folder to check against a persisted trusted-folders list, and the configuration +// directory holding that list. +// Experimental: FolderTrustIsTrustedParams is part of an experimental API and may change or +// be removed. +// Internal: FolderTrustIsTrustedParams is an internal SDK API and is not part of the public +// surface. +type FolderTrustIsTrustedParams struct { + // Configuration directory to read the trusted-folders list from. Omit it to read the + // default location; supply it when the host has redirected its configuration, or the check + // answers from a list the host is not using. + ConfigDir *string `json:"configDir,omitempty"` + // Folder to check. A folder counts as trusted when it is itself listed, when an ancestor is + // listed, or when its git root is listed. + Path string `json:"path"` +} + +// The verdict of the `forceLoginOrgs` enterprise sign-in pin for one credential. A managed +// device may pin Copilot to an approved set of GitHub organizations so a developer cannot +// sign in with a personal — or another enterprise's — account and escape every control that +// keys off the signed-in identity. A host must refuse to proceed when `allowed` is false; +// the remaining fields explain the refusal rather than qualifying it. +// Experimental: ForceLoginOrgsDecision is part of an experimental API and may change or be +// removed. +// Internal: ForceLoginOrgsDecision is an internal SDK API and is not part of the public +// surface. +type ForceLoginOrgsDecision struct { + // Whether the client may proceed. True whenever no pin is in force, so an unmanaged host is + // never blocked. + Allowed bool `json:"allowed"` + // A stable kebab-case reason code when refused: `org-not-approved`, + // `unverifiable-credential`, `policy-undetermined`, `policy-invalid`, `not-signed-in`, or + // `membership-unknown`. Omitted when allowed. Treat an unrecognized code as a refusal + // rather than mapping it to an allow: a code this host has not been taught about still + // denied the credential. + Denial *string `json:"denial,omitempty"` + // Whether a pin applies at all. Distinguishes "allowed because unmanaged" from "allowed + // because the account is approved", which a diagnostics surface needs and which also + // decides whether a bring-your-own-key provider may serve inference. + Enforced bool `json:"enforced"` + // The approved organization the account matched, when allowed under a pin. + MatchedOrg *string `json:"matchedOrg,omitempty"` + // A developer-facing explanation of the refusal. Omitted when allowed. + Message *string `json:"message,omitempty"` + // Error-level detail from managed-policy discovery — which file failed to load, which field + // failed validation. Carried on the decision rather than left to the log because a refusal + // can exit the process before a log sink exists, which would discard the only actionable + // diagnostic a misconfigured managed device produces. + PolicyDiagnostics []string `json:"policyDiagnostics"` + // The approved organization logins, as configured. Enterprise configuration rather than + // user data, so it is safe to name in a message or a log line. + RequiredOrgs []string `json:"requiredOrgs"` +} + // The remote the checked-out branch tracks. // Experimental: GitCurrentBranchRemoteResult is part of an experimental API and may change // or be removed. @@ -5142,6 +5410,8 @@ type GitHubOwnersListResult struct { // listing only. Long-abandoned unused ids can be released by later allocations. // Experimental: GitHubOwnersRequestIDResult is part of an experimental API and may change // or be removed. +// Internal: GitHubOwnersRequestIDResult is an internal SDK API and is not part of the +// public surface. type GitHubOwnersRequestIDResult struct { // Request id to pass to `gitHubOwners.list` and, to abandon it, `gitHubOwners.cancel`. RequestID int64 `json:"requestId"` @@ -5389,8 +5659,6 @@ type GlobalStateLoadForConfigDirRequest struct { // Internal: GlobalStateLoadResult is an internal SDK API and is not part of the public // surface. type GlobalStateLoadResult struct { - // Whether the user has answered the prompt suggesting they install the desktop app. - AppInstallNudgeResponded *bool `json:"appInstallNudgeResponded,omitempty"` // Whether the app tip has been shown. AppTipShown *bool `json:"appTipShown,omitempty"` // Terminals the user has already been asked to set up, so the host does not ask twice. @@ -5769,6 +6037,12 @@ type HooksDiscoverRequest struct { // still contribute to each remaining row's effective enabled state. This filters sources // rather than simulating a host with no settings. ExcludeHostHooks *bool `json:"excludeHostHooks,omitempty"` + // Explicit discovery-only file-hook roots, separate from callback hooks and filesystem + // grants. Available without ambient discovery or repository file-hook enablement, subject + // to managed/hook disables. Relative paths resolve against the request's effective working + // directory when supplied. Null and omission are equivalent; an empty array contributes no + // explicit roots. + HookDirectories []string `json:"hookDirectories,omitzero"` // Optional project directory paths whose trusted repository and project-expanded plugin // hooks should be discovered. When omitted or empty, user, managed-policy, and globally // enabled installed or explicit plugin hooks are returned without project expansion. @@ -6412,6 +6686,37 @@ type InstructionDiscoveryPathList struct { Paths []InstructionDiscoveryPath `json:"paths"` } +// Experimental: InstructionsClearCacheResult is part of an experimental API and may change +// or be removed. +type InstructionsClearCacheResult struct { +} + +// Explicit discovery arguments for one location, supplied by the caller rather than derived +// from a session. +// Experimental: InstructionsDiscoverRepoSourcesRequest is part of an experimental API and +// may change or be removed. +// Internal: InstructionsDiscoverRepoSourcesRequest is an internal SDK API and is not part +// of the public surface. +type InstructionsDiscoverRepoSourcesRequest struct { + // Extra directories to scan alongside instruction directories configured in the runtime + // environment. + AdditionalDirs []string `json:"additionalDirs,omitzero"` + // Copilot configuration directory for user-level sources. An omitted or empty value uses + // the server's configuration home. + ConfigDir *string `json:"configDir,omitempty"` + // Working directory whose instruction sources should also be included. Omission adds no + // separate working-directory scope. + CwdPath *string `json:"cwdPath,omitempty"` + // When true, also discover instruction files nested below the location. + EnableChildInstructions *bool `json:"enableChildInstructions,omitempty"` + // When true, include the sources a command-line host reads. Hosts that are not the CLI omit + // it. + IsCLI *bool `json:"isCli,omitempty"` + // Repository location to discover. An empty string selects no repository scope; cwdPath can + // still supply working-directory sources. + Location string `json:"location"` +} + // Optional project paths to include in instruction discovery. // Experimental: InstructionsDiscoverRequest is part of an experimental API and may change // or be removed. @@ -6419,6 +6724,11 @@ type InstructionsDiscoverRequest struct { // When true, omit the host's instruction sources (user/home-level files and plugin rules), // leaving only repository and working-directory sources. For multitenant deployments. ExcludeHostInstructions *bool `json:"excludeHostInstructions,omitempty"` + // Explicit discovery-only instruction roots using existing SDK instruction formats, not + // filesystem grants. Available without ambient discovery, subject to instruction/managed + // disables. Relative paths resolve against the request's effective working directory when + // supplied. Null and omission are equivalent; an empty array contributes no explicit roots. + InstructionDirectories []string `json:"instructionDirectories,omitzero"` // Optional list of project directory paths to scan for repository/working-directory // instruction sources. When omitted or empty, only user-level and plugin instruction // sources are returned (no project scan). @@ -6432,6 +6742,11 @@ type InstructionsGetDiscoveryPathsRequest struct { // When true, omit the host's user-level instruction targets, leaving only repository // targets. For multitenant deployments (mirrors `discover`'s `excludeHostInstructions`). ExcludeHostInstructions *bool `json:"excludeHostInstructions,omitempty"` + // Explicit discovery-only instruction roots to enumerate, including configured missing + // directories. Relative paths resolve against the request's effective working directory + // when supplied. These are not filesystem grants or preferred creation targets. Null and + // omission are equivalent. + InstructionDirectories []string `json:"instructionDirectories,omitzero"` // Optional list of project directory paths. When omitted or empty, only the user-level // targets are returned. ProjectPaths []string `json:"projectPaths,omitzero"` @@ -6445,6 +6760,16 @@ type InstructionsGetSourcesResult struct { Sources []InstructionSource `json:"sources"` } +// The prompt the init command runs. +// Experimental: InstructionsInitPromptResult is part of an experimental API and may change +// or be removed. +// Internal: InstructionsInitPromptResult is an internal SDK API and is not part of the +// public surface. +type InstructionsInitPromptResult struct { + // Prompt text authoring repository instructions. + Prompt string `json:"prompt"` +} + // Loaded instruction source for a session, including path, content, category, location, // applicability, and optional description. // Experimental: InstructionSource is part of an experimental API and may change or be @@ -6712,6 +7037,16 @@ type LocalSessionMetadataValue struct { Summary *string `json:"summary,omitempty"` } +// Which stored location permissions were dropped because their directory is gone. +// Experimental: LocationPermissionsCleanupResult is part of an experimental API and may +// change or be removed. +// Internal: LocationPermissionsCleanupResult is an internal SDK API and is not part of the +// public surface. +type LocationPermissionsCleanupResult struct { + // Location keys whose grants were removed. Empty when every stored location still exists. + RemovedLocationKeys []string `json:"removedLocationKeys"` +} + // An account the host has signed in to, identified by the server it lives on and the login // it uses there. The same person can appear more than once when they use both github.com // and an Enterprise server. @@ -6787,6 +7122,70 @@ type ManagedMCPServerConfig struct { URL string `json:"url"` } +// Experimental: ManagedPermissionEvaluation is part of an experimental API and may change +// or be removed. +type ManagedPermissionEvaluation struct { + // Original input operation associated with this verdict. + Operation ManagedPermissionOperation `json:"operation"` + // Managed-policy decision only; never bypasses user permissions or other security controls. + Verdict ManagedPermissionVerdict `json:"verdict"` +} + +// Experimental: ManagedPermissionOperation is part of an experimental API and may change or +// be removed. +type ManagedPermissionOperation struct { + // Operation to evaluate. Currently only url is supported. + Kind ManagedPermissionOperationKind `json:"kind"` + // Absolute HTTP(S) URL with a host. Invalid URLs reject the entire evaluation batch. + URL string `json:"url"` +} + +// Reusable managed permission evaluation context. Treat permissions as runtime-owned policy +// data and retain it verbatim; it includes source-aware composition metadata. This is a +// snapshot, not a capability or approval token; only use contexts obtained from trusted +// policy sources. +// Experimental: ManagedPermissionsContext is part of an experimental API and may change or +// be removed. +type ManagedPermissionsContext struct { + // Policy could not be determined; evaluation must deny every operation. + FailClosed bool `json:"failClosed"` + // Runtime-owned composed permissions object. Absent when no permission policy is configured. + Permissions any `json:"permissions,omitempty"` +} + +// Managed policy context and ordered operations for pure, sessionless evaluation. +// Experimental: ManagedPermissionsEvaluateRequest is part of an experimental API and may +// change or be removed. +type ManagedPermissionsEvaluateRequest struct { + // Trusted, reusable managed permission policy snapshot; no policy is discovered during + // evaluation. + Context ManagedPermissionsContext `json:"context"` + // Operations to evaluate, preserving input order and duplicates. An empty batch is valid. + Operations []ManagedPermissionOperation `json:"operations"` +} + +// Ordered managed permission verdicts and the supplied policy's fail-closed posture. +// Experimental: ManagedPermissionsEvaluateResult is part of an experimental API and may +// change or be removed. +type ManagedPermissionsEvaluateResult struct { + // Managed permission policy could not be determined; every operation is denied. + FailClosed bool `json:"failClosed"` + // One verdict per input operation, in the same order, including duplicates. + Results []ManagedPermissionEvaluation `json:"results"` +} + +// Retry outcome for one plugin required by managed settings. +// Experimental: ManagedPluginRetryEntry is part of an experimental API and may change or be +// removed. +type ManagedPluginRetryEntry struct { + // Why the plugin was not prepared. Present when `status` is `failed` or `deferred`. + Error *string `json:"error,omitempty"` + // Plugin spec in "plugin@marketplace" form. + Spec string `json:"spec"` + // What the retry did for this plugin. + Status ManagedPluginRetryStatus `json:"status"` +} + // Lock state and provenance of one managed setting. // Experimental: ManagedSettingMeta is part of an experimental API and may change or be // removed. @@ -6803,6 +7202,31 @@ type ManagedSettingMeta struct { Source string `json:"source"` } +// A user settings document and the effective managed policy whose controlled entries should +// be stamped onto it. +// Experimental: ManagedSettingsApplyControlsParams is part of an experimental API and may +// change or be removed. +// Internal: ManagedSettingsApplyControlsParams is an internal SDK API and is not part of +// the public surface. +type ManagedSettingsApplyControlsParams struct { + // The effective managed policy, as composed by `managedSettings.resolveEffectiveValue`. + // Omit it on an unmanaged host: the settings then come back unchanged. + Managed any `json:"managed,omitempty"` + // The user settings document to stamp. Entries the managed layer does not name are returned + // exactly as supplied. + Settings any `json:"settings"` +} + +// The user settings with the managed layer's controlled entries stamped on. +// Experimental: ManagedSettingsApplyControlsResult is part of an experimental API and may +// change or be removed. +// Internal: ManagedSettingsApplyControlsResult is an internal SDK API and is not part of +// the public surface. +type ManagedSettingsApplyControlsResult struct { + // The resulting settings document. + SettingsJSON any `json:"settingsJson"` +} + // Experimental: ManagedSettingsClearCacheResult is part of an experimental API and may // change or be removed. type ManagedSettingsClearCacheResult struct { @@ -6865,6 +7289,151 @@ type ManagedSettingsDiagnostic struct { Severity ManagedSettingsDiagnosticSeverity `json:"severity"` } +// Inputs describing the host and account an ingestion should resolve managed policy for. +// Experimental: ManagedSettingsIngestParams is part of an experimental API and may change +// or be removed. +// Internal: ManagedSettingsIngestParams is an internal SDK API and is not part of the +// public surface. +type ManagedSettingsIngestParams struct { + // The authenticated identity to resolve policy for. Omitted before sign-in, in which case + // no server layer is fetched. + AuthInfo any `json:"authInfo,omitempty"` + // Whether to ignore a cached server policy when reading, forcing the resolution to come + // from a live fetch. + BypassCacheRead *bool `json:"bypassCacheRead,omitempty"` + // Name of the embedding client, reported to the managed-settings service as `client_id` so + // it can refuse to deliver a policy this client is too old to enforce. Omit it and the + // runtime reports a generic identity; the runtime version sent alongside is the gate that + // actually matters. + ClientName *string `json:"clientName,omitempty"` + // A device layer supplied by the host instead of discovered from the machine. Pair it with + // `hasDeviceResponse` so an explicit absence is distinguishable from an omitted override. + DeviceResponse any `json:"deviceResponse,omitempty"` + // The host's environment variables, read for cache location and policy overrides. Supplied + // by the host rather than read from the runtime process so a command that selected its own + // configuration home is honored. + Environment any `json:"environment"` + // Whether `deviceResponse` is an override at all. True with a null `deviceResponse` asserts + // the device layer is empty; false discovers it from the machine. + HasDeviceResponse *bool `json:"hasDeviceResponse,omitempty"` + // The host's home directory, used to resolve cache and managed-file locations. + HomeDirectory string `json:"homeDirectory"` + // The host's platform, in Node's `process.platform` spelling, used to locate + // platform-specific cache and policy paths. + Platform string `json:"platform"` + // Whether a configured platform policy helper may be run to produce an enforced helper + // layer. + PolicyHelperEnabled *bool `json:"policyHelperEnabled,omitempty"` + // The policy-helper invocation reason. Defaults to sessionStart. + // Internal: PolicyHelperReason is part of the SDK's internal API surface and is not + // intended for external use. + PolicyHelperReason *ManagedSettingsPolicyHelperReason `json:"policyHelperReason,omitempty"` + // Directory the policy helper runs in when this ingestion is not owned by a registered + // session. A host resolving policy before its session exists must pass this, or a + // directory-sensitive helper judges the wrong repository. + PolicyHelperWorkingDirectory *string `json:"policyHelperWorkingDirectory,omitempty"` + // Whether to run ingestion. Omit or set false to return an empty result without discovering + // or fetching policy. + SelfFetch *bool `json:"selfFetch,omitempty"` + // How this ingestion may use the on-disk server-policy cache. Defaults to the runtime's own + // policy when omitted. + // Internal: ServerPolicyCacheMode is part of the SDK's internal API surface and is not + // intended for external use. + ServerPolicyCacheMode *ManagedSettingsServerPolicyCacheMode `json:"serverPolicyCacheMode,omitempty"` + // The session whose credential store and working directory the policy helper should use. + // Omit it when no session exists yet and the process-wide store applies. + SessionID *string `json:"sessionId,omitempty"` + // User agent to send with the policy fetch. + UserAgent string `json:"userAgent"` +} + +// The managed policy layers an ingestion resolved, together with the per-layer outcome +// flags a host needs to decide how much to trust the result. The flags are reported rather +// than folded into the settings because a host must be able to distinguish an unmanaged +// device from a managed device whose policy could not be confirmed, and apply its +// fail-closed gates in the second case. +// Experimental: ManagedSettingsIngestResult is part of an experimental API and may change +// or be removed. +// Internal: ManagedSettingsIngestResult is an internal SDK API and is not part of the +// public surface. +type ManagedSettingsIngestResult struct { + // The device layer could not be read, so it is unconfirmed rather than known-absent. + DeviceLoadFailed bool `json:"deviceLoadFailed"` + // The device layer discovered from the machine, when one exists. + DeviceResponse any `json:"deviceResponse,omitempty"` + // The sandbox floor contributed by the device layer. + DeviceSandboxFloor any `json:"deviceSandboxFloor,omitempty"` + // Whether the device layer's sandbox intent could not be determined, so a consumer must not + // read its absence as permission to run unsandboxed. + DeviceSandboxUndetermined bool `json:"deviceSandboxUndetermined"` + // A device-managed policy is known to exist. + DeviceSourceFound bool `json:"deviceSourceFound"` + // Diagnostics emitted while resolving the layers, in the order they occurred. + // Internal: LogMessages is part of the SDK's internal API surface and is not intended for + // external use. + LogMessages []ManagedSettingsLogMessage `json:"logMessages"` + // The effective managed permissions layer, when the resolved policy carries one. + Permissions any `json:"permissions,omitempty"` + // Which channel registered the policy helper that produced the helper layer. + // Internal: PolicyHelperAuthority is part of the SDK's internal API surface and is not + // intended for external use. + PolicyHelperAuthority *ManagedSettingsPolicyHelperAuthority `json:"policyHelperAuthority,omitempty"` + // A policy helper is configured for this host. + PolicyHelperConfigured bool `json:"policyHelperConfigured"` + // The policy helper's failure must be treated as fail-closed rather than ignored. + PolicyHelperFailClosed bool `json:"policyHelperFailClosed"` + // The policy helper was configured but could not be run or its output could not be read. + PolicyHelperLoadFailed bool `json:"policyHelperLoadFailed"` + // The layer produced by the platform policy helper, when one ran. + PolicyHelperResponse any `json:"policyHelperResponse,omitempty"` + // A live fetch failed but a recent cached entry was served in its place, so + // `serverFetchFailed` stays false. Lets a consumer distinguish a clean fetch from a + // cache-backed fallback. + ServerCacheFallback bool `json:"serverCacheFallback"` + // Minimum client version requested by a successful response. Advisory only; the delivered + // policy remains active. + ServerClientUpdateRequested *string `json:"serverClientUpdateRequested,omitempty"` + // The server refused to deliver a policy because this client is too old to enforce it, + // carrying the user-facing sentence describing the refusal. No retry will help; the remedy + // is a client update. Gates that exist only because of managed policy should deny on this + // rather than falling back to their unmanaged default. + ServerClientUpdateRequired *string `json:"serverClientUpdateRequired,omitempty"` + // The error that made the server fetch fail, when one did. + ServerFetchError *string `json:"serverFetchError,omitempty"` + // The server fetch failed and no cached entry stood in for it, so the server layer is + // unconfirmed and fail-closed gates apply. + ServerFetchFailed bool `json:"serverFetchFailed"` + // The server fetch failed while a policy demanded a fresh confirmation, which is the strict + // posture an enterprise opted into rather than an ordinary transient failure. + ServerForcedRefreshFailure bool `json:"serverForcedRefreshFailure"` + // The synthesized fail-closed permissions layer applied in place of a policy a refused + // client may not enforce. + ServerPermissionsFloor any `json:"serverPermissionsFloor,omitempty"` + // How the server layer was resolved. + // Internal: ServerResolution is part of the SDK's internal API surface and is not intended + // for external use. + ServerResolution ManagedSettingsServerPolicyResolution `json:"serverResolution"` + // The account's server-delivered policy, when one was resolved. + ServerResponse any `json:"serverResponse,omitempty"` + // The server fetch failed in a way a later retry could succeed at, as opposed to a refusal + // that no retry will change. + ServerRetryableFailure bool `json:"serverRetryableFailure"` + // The sandbox object from a cached server policy that a forced refresh declined to serve in + // full. Folded back into the effective sandbox layer so an outage during a forced refresh + // still synthesizes the fail-closed sandbox floor from the administrator's known intent. + ServerSandboxFloor any `json:"serverSandboxFloor,omitempty"` + // A server-managed policy is known to exist even though `serverResponse` is intentionally + // unset. Consumers should OR this with the presence of `serverResponse` when deciding + // whether the device is managed, so a fresh start during an outage still applies the + // fail-closed gates instead of treating the device as unmanaged. + ServerSourceFound bool `json:"serverSourceFound"` + // No usable token was available, so no server fetch was attempted. + ServerTokenUnavailable bool `json:"serverTokenUnavailable"` + // The effective managed settings composed from every resolved layer. Omitted when no layer + // set anything, so an unmanaged host locks nothing. + Settings any `json:"settings,omitempty"` +} + // One managed-settings channel and the document it delivered. // Experimental: ManagedSettingsLayer is part of an experimental API and may change or be // removed. @@ -6878,6 +7447,80 @@ type ManagedSettingsLayer struct { Source string `json:"source"` } +// Inputs describing the host and account whose managed remote-control policy should be +// resolved. Mirrors `managedSettings.ingest`, and deliberately carries no retention +// decision: reading this gate never becomes this runtime process's startup policy. +// Experimental: ManagedSettingsLoadRemoteControlParams is part of an experimental API and +// may change or be removed. +// Internal: ManagedSettingsLoadRemoteControlParams is an internal SDK API and is not part +// of the public surface. +type ManagedSettingsLoadRemoteControlParams struct { + // The authenticated identity to resolve policy for. Omitted before sign-in, in which case + // no server layer is fetched. + AuthInfo any `json:"authInfo,omitempty"` + // Name of the embedding client, reported to the managed-settings service as `client_id` so + // it can refuse to deliver a policy this client is too old to enforce. That refusal is + // exactly what turns into an explicit `disabled` below, so a host that omits this field + // forfeits the protection. + ClientName *string `json:"clientName,omitempty"` + // A device layer supplied by the host instead of discovered from the machine. Pair it with + // `hasDeviceResponse` so an explicit absence is distinguishable from an omitted override. + DeviceResponse any `json:"deviceResponse,omitempty"` + // The host's environment variables, read for cache location and policy overrides. + Environment any `json:"environment"` + // Whether `deviceResponse` is an override at all. True with a null `deviceResponse` asserts + // the device layer is empty; false discovers it from the machine. + HasDeviceResponse *bool `json:"hasDeviceResponse,omitempty"` + // The host's home directory, used to resolve cache and managed-file locations. + HomeDirectory string `json:"homeDirectory"` + // The host's platform, in Node's `process.platform` spelling, used to locate + // platform-specific cache and policy paths. + Platform string `json:"platform"` + // Whether a configured platform policy helper may be run to produce an enforced helper + // layer. + PolicyHelperEnabled *bool `json:"policyHelperEnabled,omitempty"` + // The policy-helper invocation reason. Defaults to sessionStart. + // Internal: PolicyHelperReason is part of the SDK's internal API surface and is not + // intended for external use. + PolicyHelperReason *ManagedSettingsPolicyHelperReason `json:"policyHelperReason,omitempty"` + // Whether to run ingestion. Omit or set false to return an empty result without discovering + // or fetching policy. + SelfFetch *bool `json:"selfFetch,omitempty"` + // User agent to send with the policy fetch. + UserAgent string `json:"userAgent"` +} + +// The managed remote-control policy in force for an account, if any. An absent policy means +// the account is unmanaged for this capability, which a host reads as allowed — so absence +// here is a positive statement that no policy applies, never a stand-in for a resolution +// that failed. A resolution that cannot be completed is rejected instead. +// Experimental: ManagedSettingsLoadRemoteControlResult is part of an experimental API and +// may change or be removed. +// Internal: ManagedSettingsLoadRemoteControlResult is an internal SDK API and is not part +// of the public surface. +type ManagedSettingsLoadRemoteControlResult struct { + // The account's `remoteControl` policy document. Absent when no managed policy governs + // remote control for this account. Reported as an explicit disabled policy, rather than as + // absence, when the service refused to deliver a policy this client is too old to enforce. + RemoteControl any `json:"remoteControl,omitempty"` +} + +// One diagnostic emitted while ingesting managed settings. Returned rather than logged by +// the runtime so the host decides where ingestion diagnostics go, which matters because an +// ingestion runs before a session exists to carry a log sink. +// Experimental: ManagedSettingsLogMessage is part of an experimental API and may change or +// be removed. +// Internal: ManagedSettingsLogMessage is an internal SDK API and is not part of the public +// surface. +type ManagedSettingsLogMessage struct { + // Severity of this diagnostic. + // Internal: Level is part of the SDK's internal API surface and is not intended for + // external use. + Level ManagedSettingsLogLevel `json:"level"` + // Human-readable diagnostic text. + Message string `json:"message"` +} + // Per-key lock state and provenance for `ManagedSettingsValues`, with the same field names. // Producers emit each typed key in values and meta together; both outer objects are omitted // when no typed key is set. @@ -6894,6 +7537,59 @@ type ManagedSettingsMeta struct { Model *ManagedSettingMeta `json:"model,omitempty"` } +// The client-supplied managed settings to validate and normalize. Omitting them is valid +// and yields no policy. +// Experimental: ManagedSettingsParseClientValueParams is part of an experimental API and +// may change or be removed. +// Internal: ManagedSettingsParseClientValueParams is an internal SDK API and is not part of +// the public surface. +type ManagedSettingsParseClientValueParams struct { + // The managed settings a client is supplying for a session it is about to open. Omit them + // when the client is imposing no policy of its own. + Settings any `json:"settings,omitempty"` +} + +// The accepted managed settings, reduced to the keys a client is allowed to set. +// Experimental: ManagedSettingsParseClientValueResult is part of an experimental API and +// may change or be removed. +// Internal: ManagedSettingsParseClientValueResult is an internal SDK API and is not part of +// the public surface. +type ManagedSettingsParseClientValueResult struct { + // The normalized settings. Omitted when the client supplied none. An accepted document that + // sets nothing comes back as an empty object, which is distinct from omission. + SettingsJSON any `json:"settingsJson,omitempty"` +} + +// Ordered managed permission verdicts and the supplied policy's fail-closed posture. +// Experimental: ManagedSettingsPermissionsEvaluateResult is part of an experimental API and +// may change or be removed. +type ManagedSettingsPermissionsEvaluateResult struct { + // Managed permission policy could not be determined; every operation is denied. + FailClosed bool `json:"failClosed"` + // One verdict per input operation, in the same order, including duplicates. + Results []ManagedPermissionEvaluation `json:"results"` +} + +// The device-managed settings layer, with the sandbox floor and the load outcome a host +// needs to compose it into an effective policy. +// Experimental: ManagedSettingsReadDeviceResult is part of an experimental API and may +// change or be removed. +// Internal: ManagedSettingsReadDeviceResult is an internal SDK API and is not part of the +// public surface. +type ManagedSettingsReadDeviceResult struct { + // Whether discovery or validation failed. A host must treat this as "policy unknown" rather + // than "no policy": absent settings with this set false mean the device genuinely has none. + LoadFailed bool `json:"loadFailed"` + // Sandbox floor read from the discovered document before validation, so a document that is + // otherwise rejected can still raise the floor it declared. + SandboxFloor any `json:"sandboxFloor,omitempty"` + // Validated, canonical device-managed settings. Omitted when no device policy was + // discovered, when the discovered document failed validation, or when it validated to an + // empty object — an empty document carries no policy, and reporting it as present would + // outrank layers that do. + Settings any `json:"settings,omitempty"` +} + // Validated device-managed settings discovered before a session exists. // Experimental: ManagedSettingsReadResult is part of an experimental API and may change or // be removed. @@ -6905,6 +7601,53 @@ type ManagedSettingsReadResult struct { SettingsJSON any `json:"settingsJson,omitempty"` } +// The bypass-permissions state a host holds today, the account now signed in, and what the +// managed-policy resolution said. +// Experimental: ManagedSettingsResolveBypassDisableTransitionParams is part of an +// experimental API and may change or be removed. +// Internal: ManagedSettingsResolveBypassDisableTransitionParams is an internal SDK API and +// is not part of the public surface. +type ManagedSettingsResolveBypassDisableTransitionParams struct { + // The account signed in now. A `managed` disable pinned to a different account is lifted. + ActiveIdentity string `json:"activeIdentity"` + // The account a `managed` disable is pinned to. Omit it when the current source is not + // `managed`; it is ignored for the other sources. + CurrentManagedIdentity *string `json:"currentManagedIdentity,omitempty"` + // Which layer currently disables bypass mode. Omit it when nothing does. + // Internal: CurrentSource is part of the SDK's internal API surface and is not intended for + // external use. + CurrentSource *ManagedSettingsBypassDisableSource `json:"currentSource,omitempty"` + // What the resolution said about bypass mode. Send `unknown` rather than omitting the field + // when there is no verdict. + // Internal: Policy is part of the SDK's internal API surface and is not intended for + // external use. + Policy ManagedSettingsBypassPolicyResolution `json:"policy"` +} + +// The bypass-permissions state a host should hold after the resolution, and what it must do +// to get there. +// Experimental: ManagedSettingsResolveBypassDisableTransitionResult is part of an +// experimental API and may change or be removed. +// Internal: ManagedSettingsResolveBypassDisableTransitionResult is an internal SDK API and +// is not part of the public surface. +type ManagedSettingsResolveBypassDisableTransitionResult struct { + // The account the returned `managed` source is pinned to. Store it alongside `source`; the + // next call needs it to decide whether an account switch lifts the disable. + ManagedIdentity *string `json:"managedIdentity,omitempty"` + // The value to store in the host's own bypass-disabled flag. Omitted means leave it exactly + // as it is, which is not the same as being told to store `false`. + SetDisabledState *bool `json:"setDisabledState,omitempty"` + // Which layer disables bypass mode after this resolution. Omitted when none does, which is + // what a lifted disable looks like. + // Internal: Source is part of the SDK's internal API surface and is not intended for + // external use. + Source *ManagedSettingsBypassDisableSource `json:"source,omitempty"` + // Whether a bypass-permissions session already running must be turned off. True only when + // this resolution newly disables bypass mode, so a host that is already compliant is not + // interrupted. + SuppressRuntimeAllowAll bool `json:"suppressRuntimeAllowAll"` +} + // Effective enterprise managed settings and contributing channels. Session events report // applied policy; sessionless resolve reports an account/device snapshot, and compose // reports a non-applying preview of candidate documents. Device values take precedence over @@ -6914,16 +7657,18 @@ type ManagedSettingsReadResult struct { // Experimental: ManagedSettingsResolvedData is part of an experimental API and may change // or be removed. type ManagedSettingsResolvedData struct { - // Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. - // Deny-wins across layers, and forced on when `failClosed` is true. + // Whether an explicit enterprise policy restriction disables bypass-permissions ("yolo") + // mode for this session. Deny-wins across layers; an unresolved policy does not force this + // on by itself. BypassPermissionsDisabled bool `json:"bypassPermissionsDisabled"` // Whether a session-local permissions layer injected by the SDK host was present ClientManaged *bool `json:"clientManaged,omitempty"` // Whether an actual device MDM/plist/registry/file managed-settings layer was present DeviceManaged bool `json:"deviceManaged"` - // Whether managed policy could not be determined (e.g. a failed server fetch) and the - // session fell back to the fail-closed restriction. When true, restrictions such as - // disabling bypass-permissions are enforced even though `settings` may be absent. + // Whether managed policy could not be determined (e.g. a failed server fetch) and + // unresolved-policy safeguards remain active. This does not by itself disable + // bypass-permissions; `bypassPermissionsDisabled` reports only an explicit policy + // restriction. FailClosed bool `json:"failClosed"` // The setting keys under enterprise management in the effective managed settings (e.g. // `model`, `enabledPlugins`, `permissions`). Empty when no managed settings are in force. @@ -6931,6 +7676,10 @@ type ManagedSettingsResolvedData struct { // Whether at least two managed sources supplied permission allowlists, so enforcement // intersects them and the flattened settings payload omits `permissions.allow`. PermissionsAllowIntersected *bool `json:"permissionsAllowIntersected,omitempty"` + // Reusable retained managed permission policy for managedSettings.permissions.evaluate, + // including source composition and session-local injection. Provided by live session + // snapshots; absent in older events. Refresh the context when managed policy changes. + PermissionsContext *ManagedPermissionsContext `json:"permissionsContext,omitempty"` // Whether the policy-helper managed-settings layer was present. The policy helper is the // weakest channel: it fills keys no enterprise source set and can never replace one. PolicyHelperManaged *bool `json:"policyHelperManaged,omitempty"` @@ -6950,6 +7699,54 @@ type ManagedSettingsResolvedData struct { Source ManagedSettingsResolvedSource `json:"source"` } +// The credential and environment to evaluate the device-channel sign-in pin against. +// Experimental: ManagedSettingsResolveDeviceForceLoginOrgsGateParams is part of an +// experimental API and may change or be removed. +// Internal: ManagedSettingsResolveDeviceForceLoginOrgsGateParams is an internal SDK API and +// is not part of the public surface. +type ManagedSettingsResolveDeviceForceLoginOrgsGateParams struct { + // The authenticated identity to judge. Omitted when signed out, which is the case the + // device channel exists to serve: it can refuse a first sign-in before any credential + // exists. + AuthInfo any `json:"authInfo,omitempty"` + // The host's environment variables. Read for the variables that would make an identity's + // membership snapshot forgeable, so a credential whose provenance cannot be trusted is + // refused rather than accepted. + Environment any `json:"environment"` +} + +// The managed-settings layers to compose into an effective policy. Every layer is optional; +// omitting all of them yields no effective policy. +// Experimental: ManagedSettingsResolveEffectiveValueParams is part of an experimental API +// and may change or be removed. +// Internal: ManagedSettingsResolveEffectiveValueParams is an internal SDK API and is not +// part of the public surface. +type ManagedSettingsResolveEffectiveValueParams struct { + // The device sandbox floor, folded into the device layer under `sandbox` before composition. + DeviceSandboxFloor any `json:"deviceSandboxFloor,omitempty"` + // The device layer: native MDM and managed-file settings describing the machine rather than + // the account. + Mdm any `json:"mdm,omitempty"` + // The policy-helper layer, an enforced channel sourced from a platform policy helper + // process. + PolicyHelper any `json:"policyHelper,omitempty"` + // The account's server-delivered org policy, as returned by a managed-settings fetch. + // Omitted before sign-in or when the fetch has not landed. + Server any `json:"server,omitempty"` +} + +// The effective managed policy composed from the supplied layers. +// Experimental: ManagedSettingsResolveEffectiveValueResult is part of an experimental API +// and may change or be removed. +// Internal: ManagedSettingsResolveEffectiveValueResult is an internal SDK API and is not +// part of the public surface. +type ManagedSettingsResolveEffectiveValueResult struct { + // The composed effective managed settings. Omitted when no layer sets anything, so an + // unmanaged host locks nothing. Carries `sandboxAdvisoryFloorDeclined: true` when an + // advisory server force-on was dropped because this host cannot run the sandbox. + SettingsJSON any `json:"settingsJson,omitempty"` +} + // Experimental: ManagedSettingsResolveRequest is part of an experimental API and may change // or be removed. type ManagedSettingsResolveRequest struct { @@ -6985,6 +7782,11 @@ type ManagedSettingsResolveResult struct { Layers []ManagedSettingsLayer `json:"layers"` // Per-key lock state and provenance for the entries in `values`, using the same key names. Meta *ManagedSettingsMeta `json:"meta,omitempty"` + // Reusable source-composed permission policy for managedSettings.permissions.evaluate. + // Unlike resolved.settings.permissions, this retains every source's allowlist and + // default-prompt semantics. Refresh through resolve when account/device policy changes. + // Absent on runtimes that do not support permission evaluation. + PermissionsContext *ManagedPermissionsContext `json:"permissionsContext,omitempty"` // Effective managed settings from the device and account (server) channels, in the same // shape as `session.managedSettings.get`, excluding session-local injection. Resolved ManagedSettingsResolvedData `json:"resolved"` @@ -6995,6 +7797,197 @@ type ManagedSettingsResolveResult struct { Values *ManagedSettingsValues `json:"values,omitempty"` } +// Inputs describing the host and account a startup policy resolution should run for. +// Mirrors `managedSettings.ingest`, and adds the decision of whether the resolved policy is +// retained for this runtime process. +// Experimental: ManagedSettingsResolveStartupPolicyParams is part of an experimental API +// and may change or be removed. +// Internal: ManagedSettingsResolveStartupPolicyParams is an internal SDK API and is not +// part of the public surface. +type ManagedSettingsResolveStartupPolicyParams struct { + // The authenticated identity to resolve policy for. Omitted before sign-in, in which case + // no server layer is fetched and the sign-in pin is evaluated against no credential. + AuthInfo any `json:"authInfo,omitempty"` + // Whether to ignore a cached server policy when reading, forcing the resolution to come + // from a live fetch. + BypassCacheRead *bool `json:"bypassCacheRead,omitempty"` + // Name of the embedding client, reported to the managed-settings service as `client_id` so + // it can refuse to deliver a policy this client is too old to enforce. + ClientName *string `json:"clientName,omitempty"` + // A device layer supplied by the host instead of discovered from the machine. Pair it with + // `hasDeviceResponse` so an explicit absence is distinguishable from an omitted override. + DeviceResponse any `json:"deviceResponse,omitempty"` + // The host's environment variables, read for cache location, policy overrides, and the + // variables that would make a membership snapshot forgeable. + Environment any `json:"environment"` + // Whether `deviceResponse` is an override at all. True with a null `deviceResponse` asserts + // the device layer is empty; false discovers it from the machine. + HasDeviceResponse *bool `json:"hasDeviceResponse,omitempty"` + // The host's home directory, used to resolve cache and managed-file locations. + HomeDirectory string `json:"homeDirectory"` + // The host's platform, in Node's `process.platform` spelling, used to locate + // platform-specific cache and policy paths. + Platform string `json:"platform"` + // Whether a configured platform policy helper may be run to produce an enforced helper + // layer. + PolicyHelperEnabled *bool `json:"policyHelperEnabled,omitempty"` + // The policy-helper invocation reason. Defaults to sessionStart. + // Internal: PolicyHelperReason is part of the SDK's internal API surface and is not + // intended for external use. + PolicyHelperReason *ManagedSettingsPolicyHelperReason `json:"policyHelperReason,omitempty"` + // Directory the policy helper runs in when this resolution is not owned by a registered + // session. A host resolving policy before its session exists must pass this, or a + // directory-sensitive helper judges the wrong repository. + PolicyHelperWorkingDirectory *string `json:"policyHelperWorkingDirectory,omitempty"` + // Whether to retain the resolved policy as this runtime process's startup policy, so later + // work in the process enforces it. Set false to resolve without side effects, which is what + // a protocol host wants before session creation: its sessions apply the complete policy + // through the normal lifecycle, and retaining here would let a readiness credential affect + // unrelated sessions. The returned verdict is identical either way — this decides only + // whether the process keeps it. + RetainAndApply bool `json:"retainAndApply"` + // Whether to run ingestion. Omit or set false to return an empty result without discovering + // or fetching policy. + SelfFetch *bool `json:"selfFetch,omitempty"` + // How this resolution may use the on-disk server-policy cache. Defaults to the runtime's + // own policy when omitted. Rejected when combined with `bypassCacheRead`, because the two + // express the same decision and accepting both would leave the caller unsure which one the + // runtime honored. + // Internal: ServerPolicyCacheMode is part of the SDK's internal API surface and is not + // intended for external use. + ServerPolicyCacheMode *ManagedSettingsServerPolicyCacheMode `json:"serverPolicyCacheMode,omitempty"` + // The session whose credential store, working directory and client kind the resolution + // should use. Omit it when no session exists yet and the process-wide store applies. A + // resolution whose session names a credential other than `authInfo` is returned but not + // retained, so a stale sign-in cannot apply policy for an account that has already been + // switched away from. + SessionID *string `json:"sessionId,omitempty"` + // User agent to send with the policy fetch. + UserAgent string `json:"userAgent"` +} + +// The startup policy a resolution composed, the per-layer outcome flags a host needs to +// decide how much to trust it, and the enterprise sign-in verdict for the credential it ran +// against. The flags are reported rather than folded into the settings because a host must +// distinguish an unmanaged device from a managed device whose policy could not be +// confirmed, and apply its fail-closed gates in the second case. +// Experimental: ManagedSettingsResolveStartupPolicyResult is part of an experimental API +// and may change or be removed. +// Internal: ManagedSettingsResolveStartupPolicyResult is an internal SDK API and is not +// part of the public surface. +type ManagedSettingsResolveStartupPolicyResult struct { + // Advisory minimum client version from a successful server response. + ClientUpdateRequested *string `json:"clientUpdateRequested,omitempty"` + // The server refused to deliver a policy because this client is too old to enforce it, + // carrying the user-facing sentence describing the refusal. No retry will help; the remedy + // is a client update. + ClientUpdateRequired *string `json:"clientUpdateRequired,omitempty"` + // The device layer could not be read, so it is unconfirmed rather than known-absent. + DeviceLoadFailed bool `json:"deviceLoadFailed"` + // The device layer discovered from the machine, when one exists. + DeviceResponse any `json:"deviceResponse,omitempty"` + // The sandbox floor contributed by the device layer. + DeviceSandboxFloor any `json:"deviceSandboxFloor,omitempty"` + // The enterprise sign-in verdict for the credential this resolution ran against, evaluated + // over the effective policy so a server-delivered pin is honored alongside a device one. + // Computed on every resolution, including one that was not retained, so a host that + // declines to apply policy still cannot proceed past a pin. + // Internal: ForceLoginOrgs is part of the SDK's internal API surface and is not intended + // for external use. + ForceLoginOrgs ForceLoginOrgsDecision `json:"forceLoginOrgs"` + // A managed policy is known to apply to this host, from a discovered source or a resolved + // document. + ManagedPolicyPresent bool `json:"managedPolicyPresent"` + // The permissions layer specifically could not be confirmed, so permission gates apply + // their fail-closed posture. + PermissionPolicyUndetermined bool `json:"permissionPolicyUndetermined"` + // The policy helper was configured but could not be run, or its output could not be read. + PolicyHelperLoadFailed bool `json:"policyHelperLoadFailed"` + // The layer produced by the platform policy helper, when one ran. + PolicyHelperResponse any `json:"policyHelperResponse,omitempty"` + // Some layer of the policy could not be confirmed, so its absence must not be read as + // permission. Folds in a failed server fetch as well as an unreadable device layer. + PolicyUndetermined bool `json:"policyUndetermined"` + // The sandbox was enabled because policy could not be confirmed rather than because an + // administrator asked for it. Lets a host explain a sandbox the user did not configure. + SandboxEnabledByUndeterminedPolicy bool `json:"sandboxEnabledByUndeterminedPolicy"` + // The effective sandbox floor composed across every layer, which a host must apply rather + // than its own default. + SandboxFloor any `json:"sandboxFloor,omitempty"` + // The server fetch failed and no cached entry stood in for it, so the server layer is + // unconfirmed and fail-closed gates apply. + ServerFetchFailed bool `json:"serverFetchFailed"` + // How the current runtime resolved the server layer. + // Internal: ServerResolution is part of the SDK's internal API surface and is not intended + // for external use. + ServerResolution ManagedSettingsServerPolicyResolution `json:"serverResolution"` + // The account's server-delivered policy, when one was resolved. + ServerResponse any `json:"serverResponse,omitempty"` + // Telemetry describing which layers answered and how, for the host to report alongside its + // own startup events. + Telemetry any `json:"telemetry"` + // Warnings raised while resolving, in the order they occurred. + Warnings []string `json:"warnings"` +} + +// The managed layers to retain as this process's startup policy, with the outcome of +// fetching each one. +// Experimental: ManagedSettingsRetainPolicyParams is part of an experimental API and may +// change or be removed. +// Internal: ManagedSettingsRetainPolicyParams is an internal SDK API and is not part of the +// public surface. +type ManagedSettingsRetainPolicyParams struct { + // Whether the device load failed. Report it honestly, for the same reason as + // `serverFetchFailed`. + DeviceLoadFailed bool `json:"deviceLoadFailed"` + // The device/MDM policy layer. + DeviceResponse any `json:"deviceResponse,omitempty"` + // Sandbox floor declared by the device layer, carried separately so a document that failed + // validation can still raise the floor. + DeviceSandboxFloor any `json:"deviceSandboxFloor,omitempty"` + // Whether the device sandbox intent is unreadable independently of its other settings. + DeviceSandboxUndetermined bool `json:"deviceSandboxUndetermined"` + // Whether a device policy source demonstrably exists. This, not a read error, is the + // evidence a device is managed, so a fail-closed policy gates on it. + DeviceSourceFound bool `json:"deviceSourceFound"` + // Whether any managed policy source exists, independently of fetch failures. + ManagedPolicyPresent bool `json:"managedPolicyPresent"` + // Whether permission policy is undetermined. Sandbox-only uncertainty does not imply this + // flag. + PermissionPolicyUndetermined bool `json:"permissionPolicyUndetermined"` + // The policy helper's response: the third managed layer that a process owning its startup + // policy resolves. It is retained with the other two so that every ambient read in this + // process, including the mutation guards and the governed marketplace allowlist that the + // `plugins.*` handlers derive, sees the whole policy the process fetched. A session-owning + // process omits it, because it resolves a helper per session against that session's working + // directory. + PolicyHelperResponse any `json:"policyHelperResponse,omitempty"` + // Preserve the retained server layer during a device-only resolution. + PreserveServerState bool `json:"preserveServerState"` + // Whether uncertainty alone enabled the shared sandbox floor. + SandboxEnabledByUndeterminedPolicy bool `json:"sandboxEnabledByUndeterminedPolicy"` + // Whether the shared sandbox policy is undetermined. Diagnostic errors alone do not imply + // this flag. + SandboxFloorUndetermined bool `json:"sandboxFloorUndetermined"` + // Whether the server fetch failed. Report it honestly: true keeps the already-retained + // server layer rather than replacing it with an absent one. + ServerFetchFailed bool `json:"serverFetchFailed"` + // Whether a required fresh server-policy confirmation failed. + ServerForcedRefreshFailure bool `json:"serverForcedRefreshFailure"` + // The resolved server policy. A known source can lack a response after a forced-refresh + // failure. + ServerResponse any `json:"serverResponse,omitempty"` + // Whether the server policy source exists, even when its response is unavailable. + ServerSourceFound bool `json:"serverSourceFound"` + // Shared server/device sandbox floor, excluding session-local helper policy. + SharedSandboxFloor any `json:"sharedSandboxFloor,omitempty"` +} + +// Experimental: ManagedSettingsRetainPolicyResult is part of an experimental API and may +// change or be removed. +type ManagedSettingsRetainPolicyResult struct { +} + // The authoring JSON schema for managed settings recognized by this runtime. // Experimental: ManagedSettingsSchemaResult is part of an experimental API and may change // or be removed. @@ -7493,6 +8486,8 @@ type MCPConfiguredServerState struct { // Credential-free authentication identity used to configure GitHub MCP. // Experimental: MCPConfigureGitHubRequest is part of an experimental API and may change or // be removed. +// Internal: MCPConfigureGitHubRequest is an internal SDK API and is not part of the public +// surface. type MCPConfigureGitHubRequest struct { // Opaque runtime auth info for GitHub MCP configuration. Marked internal: an in-process // runtime shape (configureGitHubMcp is a no-op over the wire). @@ -8301,6 +9296,17 @@ type MCPIsServerRunningResult struct { Running bool `json:"running"` } +// Names of the MCP servers the runtime supplies itself. +// Experimental: MCPListBuiltinServerNamesResult is part of an experimental API and may +// change or be removed. +// Internal: MCPListBuiltinServerNamesResult is an internal SDK API and is not part of the +// public surface. +type MCPListBuiltinServerNamesResult struct { + // Every built-in server name. A host disabling the built-ins wholesale suppresses exactly + // this set. + Names []string `json:"names"` +} + // Server name whose tool list should be returned. // Experimental: MCPListToolsRequest is part of an experimental API and may change or be // removed. @@ -9213,6 +10219,8 @@ type MCPPromptsListResult struct { // Registration parameters for an external MCP client. // Experimental: MCPRegisterExternalClientRequest is part of an experimental API and may // change or be removed. +// Internal: MCPRegisterExternalClientRequest is an internal SDK API and is not part of the +// public surface. type MCPRegisterExternalClientRequest struct { // In-process MCP Client instance. Marked internal: cannot be serialized across the JSON-RPC // boundary. @@ -9233,8 +10241,80 @@ type MCPRegisterExternalClientRequest struct { Transport any `json:"transport"` } +// Registry search to abandon. +// Experimental: MCPRegistryCancelRequest is part of an experimental API and may change or +// be removed. +// Internal: MCPRegistryCancelRequest is an internal SDK API and is not part of the public +// surface. +type MCPRegistryCancelRequest struct { + // Request ID from `mcp.registry.allocateRequestId` that the search uses. + RequestID int64 `json:"requestId"` +} + +// Whether the cancel reached a live search. +// Experimental: MCPRegistryCancelResult is part of an experimental API and may change or be +// removed. +// Internal: MCPRegistryCancelResult is an internal SDK API and is not part of the public +// surface. +type MCPRegistryCancelResult struct { + // True when the cancel stopped a running search. False for unknown or reclaimed IDs, + // completed or canceled searches, and unused reservations. The cancel releases an unused + // reservation. + Canceled bool `json:"canceled"` +} + +// Request id naming a cancellable registry search. +// Experimental: MCPRegistryRequestIDResult is part of an experimental API and may change or +// be removed. +// Internal: MCPRegistryRequestIDResult is an internal SDK API and is not part of the public +// surface. +type MCPRegistryRequestIDResult struct { + // Request ID for `mcp.registry.search` and `mcp.registry.cancel`. It serves one search. + // Allocation can reclaim unused IDs at the 1,024-ID reservation limit. + RequestID int64 `json:"requestId"` +} + +// Registry search terms, the credential to search under, and the request id that makes the +// search cancellable. +// Experimental: MCPRegistrySearchRequest is part of an experimental API and may change or +// be removed. +// Internal: MCPRegistrySearchRequest is an internal SDK API and is not part of the public +// surface. +type MCPRegistrySearchRequest struct { + // The credential the search runs under, carried opaquely. Hosts usually send + // credential-free `AuthIdentity`; a `token` identity can be resolved only when it embeds a + // token, while `env` and `gh-cli` identities can use a token embedded in the request first. + AuthInfo any `json:"authInfo"` + // Maximum number of servers to return. + Limit int64 `json:"limit"` + // Free-text query. Omitted or empty asks the registry for its top servers rather than + // searching. A value that is not a string is refused. + Query *string `json:"query,omitempty"` + // Repository used for the policy lookup, as `owner/name`. The policy selects the registry + // URL and whether the user token goes to the registry. The registry receives this + // repository only when the policy entry lists it as required context. A value that is not a + // string is refused. + Repository *string `json:"repository,omitempty"` + // Request ID from `mcp.registry.allocateRequestId`. The search refuses unknown, reclaimed, + // or canceled IDs and IDs that another search or request already uses. + RequestID int64 `json:"requestId"` +} + +// Servers selected from the registry response. +// Experimental: MCPRegistrySearchResult is part of an experimental API and may change or be +// removed. +// Internal: MCPRegistrySearchResult is an internal SDK API and is not part of the public +// surface. +type MCPRegistrySearchResult struct { + // The server objects, carried opaquely. The runtime follows pages, keeps the newest entry + // per server name, cuts the list to `limit`, and sorts an empty-query result by GitHub + // stars. Each server object remains unchanged because the registry owns that shape. + Servers any `json:"servers"` +} + // In-process MCP reload configuration. // Experimental: MCPReloadConfig is part of an experimental API and may change or be removed. +// Internal: MCPReloadConfig is an internal SDK API and is not part of the public surface. type MCPReloadConfig struct { ActiveGitHubToken *string `json:"activeGitHubToken,omitempty"` // Server names the CLI enabled for this session via `--enable-mcp-server`. @@ -9255,6 +10335,8 @@ type MCPReloadConfig struct { // Opaque MCP reload configuration. // Experimental: MCPReloadWithConfigRequest is part of an experimental API and may change or // be removed. +// Internal: MCPReloadWithConfigRequest is an internal SDK API and is not part of the public +// surface. type MCPReloadWithConfigRequest struct { // Opaque runtime MCP reload configuration. Marked internal: an in-process runtime shape // (reloadMcpServers throws over the wire). @@ -9760,6 +10842,8 @@ func (MCPServerConfigHTTP) mcpServerConfig() {} // In-process MCP server configuration used by embedded SDK clients. // Experimental: MCPServerConfigMemory is part of an experimental API and may change or be // removed. +// Internal: MCPServerConfigMemory is an internal SDK API and is not part of the public +// surface. type MCPServerConfigMemory struct { // Configuration warnings recorded while loading the server. ConfigWarnings []string `json:"configWarnings,omitzero"` @@ -9880,6 +10964,38 @@ type MCPSetEnvValueModeResult struct { Mode MCPSetEnvValueModeDetails `json:"mode"` } +// The session tool filters that decide whether the session still has a shell to run `gh` +// with. +// Experimental: MCPShouldExcludeGitHubToolsRequest is part of an experimental API and may +// change or be removed. +// Internal: MCPShouldExcludeGitHubToolsRequest is an internal SDK API and is not part of +// the public surface. +type MCPShouldExcludeGitHubToolsRequest struct { + // The session's tool allowlist, when it set one. Omitted means the session constrains + // nothing this way. + AvailableTools []string `json:"availableTools,omitzero"` + // The session's tool denylist, when it set one. Omitted means the session constrains + // nothing this way. + ExcludedTools []string `json:"excludedTools,omitzero"` + // How the allowlist and denylist combine when both are set. Omitted means the default every + // session gets, so a caller that never chose a precedence is answered as its sessions + // behave. + ToolFilterPrecedence *OptionsUpdateToolFilterPrecedence `json:"toolFilterPrecedence,omitempty"` +} + +// Whether the gh-replaceable GitHub MCP tools may be clipped for the session described by +// the request. +// Experimental: MCPShouldExcludeGitHubToolsResult is part of an experimental API and may +// change or be removed. +// Internal: MCPShouldExcludeGitHubToolsResult is an internal SDK API and is not part of the +// public surface. +type MCPShouldExcludeGitHubToolsResult struct { + // True only when both halves hold: the session's filters still reach the platform shell + // tool, and the host actually has the `gh` those tools would be replaced by. Feed it + // straight back as the `excludeGhReplaceableTools` build option. + ExcludeGhReplaceableTools bool `json:"excludeGhReplaceableTools"` +} + // Concrete configuration file containing an MCP server declaration. // Experimental: MCPSourceFile is part of an experimental API and may change or be removed. type MCPSourceFile struct { @@ -10006,6 +11122,8 @@ type MCPUninstallPlan struct { // Server name identifying the external client to remove. // Experimental: MCPUnregisterExternalClientRequest is part of an experimental API and may // change or be removed. +// Internal: MCPUnregisterExternalClientRequest is an internal SDK API and is not part of +// the public surface. type MCPUnregisterExternalClientRequest struct { // Server name of the external client to unregister ServerName string `json:"serverName"` @@ -10546,6 +11664,68 @@ type ModelClearStartupSeedResult struct { Cleared bool `json:"cleared"` } +// Host presentation state the reasoning-summaries policy depends on. +// Experimental: ModelGetReasoningSummariesRequest is part of an experimental API and may +// change or be removed. +type ModelGetReasoningSummariesRequest struct { + // The host's persisted `showReasoning` user setting, omitted when unset. + PersistedShowReasoning *bool `json:"persistedShowReasoning,omitempty"` + // Whether the host is currently showing reasoning summaries. Defaults to false. + SummariesVisible *bool `json:"summariesVisible,omitempty"` +} + +// Reasoning-summaries presentation policy for the session's current model. +// Experimental: ModelGetReasoningSummariesResult is part of an experimental API and may +// change or be removed. +type ModelGetReasoningSummariesResult struct { + // Whether the supplied `persistedShowReasoning` setting only restates the model default. + // This reports a redundancy, not a standing instruction to delete the setting: it stays + // true for as long as the setting and the model default agree, including for a user who + // later chose that value deliberately. It exists for a one-time migration of a legacy + // setting written before the model default made it redundant, so a host that acts on it + // owns the cleanup and must gate it behind its own persisted `done` marker, delete only a + // setting whose value still matches the one it read, and never repeat the deletion on a + // later launch. Under an off-by-default reasoning-summaries experiment an explicit + // `showReasoning: true` opt-in also reads as redundant on a summary-producing model, so an + // ungated host would keep deleting that opt-in and hide summaries again on every launch. + ClearRedundantSetting bool `json:"clearRedundantSetting"` + // Whether the session's reasoning-summaries experiment assignment has settled. False means + // this read does not wait for a pending assignment and reports the experiment-derived + // fields conservatively: `showHiddenHint` is false and `visibility` falls back to + // `persistedShowReasoning`, or is omitted when none was supplied. A host that wants the + // assigned answer re-reads until this is true. + ExperimentSettled bool `json:"experimentSettled"` + // Whether the selected model shows reasoning summaries by default. + ModelDefaultShowsSummaries bool `json:"modelDefaultShowsSummaries"` + // False until the session has a model catalog and a selected model. This read never + // resolves the catalog itself, and the runtime emits no readiness notification, so + // repeating this call alone leaves `ready` false indefinitely on a session that has neither + // listed models nor run a turn. `session.model.list` is the prerequisite and the signal: it + // resolves and caches the catalog for the session, so a successful return means the next + // read is ready. The exception is a session that resolves models without a Copilot catalog, + // that is a BYOK registry or an alternate provider: `session.model.list` returns an empty + // list without caching a catalog, so `ready` stays false and this policy is unavailable + // there. While false, every model-policy field is false and `visibility` is omitted; + // `experimentSettled` is independent of readiness and still reports the assignment state. + Ready bool `json:"ready"` + // Whether turns on this session produce reasoning summaries, honouring an explicit session + // reasoning-summary override. + SessionShowsSummaries bool `json:"sessionShowsSummaries"` + // Whether to show the hint that reasoning summaries are hidden. Always false while + // `experimentSettled` is false. + ShowHiddenHint bool `json:"showHiddenHint"` + // Whether the selected model emits reasoning headers. + ShowReasoningHeaders bool `json:"showReasoningHeaders"` + // Default reasoning visibility, and only meaningful once `ready` is true: false when the + // session produces no summaries, else the persisted setting, else the experiment's default. + // While `ready` is true it is omitted only when all three are unavailable, that is when the + // session produces summaries, no `persistedShowReasoning` was supplied, and + // `experimentSettled` is false. While `ready` is false there is no model policy to report + // and this field is always omitted, so an absent value alone does not imply a pending + // experiment. + Visibility *bool `json:"visibility,omitempty"` +} + // List of Copilot models available to the resolved user, including capabilities and billing // metadata. // Experimental: ModelList is part of an experimental API and may change or be removed. @@ -12147,6 +13327,12 @@ type PermissionRecoveryData struct { // Policy selected from the current client's response capability; mode or client changes may // update it during recovery OnBlocked PermissionRecoveryOnBlocked `json:"onBlocked"` + // Subagent identifier that owns this recovery episode when the request was delegated + // through a parent permission service + OwnerAgentID *string `json:"ownerAgentId,omitempty"` + // Root or child execution-session identifier that owns this recovery episode; omitted for + // legacy snapshots + OwnerSessionID *string `json:"ownerSessionId,omitempty"` // Controlled reason for the latest episode transition Reason PermissionRecoveryReason `json:"reason"` // Current lifecycle state of the recovery episode @@ -12260,6 +13446,32 @@ type PermissionsConfigureResult struct { Success bool `json:"success"` } +// The verbatim managed `permissions` slice to interpret. +// Experimental: PermissionsEvaluateManagedPolicyParams is part of an experimental API and +// may change or be removed. +// Internal: PermissionsEvaluateManagedPolicyParams is an internal SDK API and is not part +// of the public surface. +type PermissionsEvaluateManagedPolicyParams struct { + // The `permissions` object from a managed settings layer, for example + // `{"disableBypassPermissionsMode":"disable"}`. Omitted or null means no managed policy + // applies. + Permissions any `json:"permissions,omitempty"` +} + +// The policy questions a host asks before granting elevated permissions. +// Experimental: PermissionsEvaluateManagedPolicyResult is part of an experimental API and +// may change or be removed. +type PermissionsEvaluateManagedPolicyResult struct { + // True when managed policy forbids bypass-permissions mode, so allow-all style flags must + // not take effect. + BypassPermissionsDisabled bool `json:"bypassPermissionsDisabled"` + // True when the managed layer supplies permission rules that the session must enforce. + PermissionRulesActive bool `json:"permissionRulesActive"` + // True when a managed policy exists but this build could not determine its meaning. Hosts + // fail closed on it rather than assuming permission was granted. + PolicyUndetermined bool `json:"policyUndetermined"` +} + // Indicates whether the operation succeeded. // Experimental: PermissionsFolderTrustAddTrustedResult is part of an experimental API and // may change or be removed. @@ -12639,6 +13851,31 @@ type PermissionURLsSetUnrestrictedModeParams struct { Enabled bool `json:"enabled"` } +// A complete JSON snapshot to write under the runtime's per-path file lock. +// Experimental: PersistenceWriteRawJSONLockedRequest is part of an experimental API and may +// change or be removed. +type PersistenceWriteRawJSONLockedRequest struct { + // The serialized JSON document to write. It replaces the file's contents entirely; nothing + // is merged. + DataJSON string `json:"dataJson"` + // Comment lines to emit above the document, for a format that tolerates them. Omit for + // strict JSON. + FileHeader *string `json:"fileHeader,omitempty"` + // Absolute path of the file to replace. Parent directories are created when missing. + Path string `json:"path"` +} + +// The outcome of a locked snapshot write. +// Experimental: PersistenceWriteRawJSONLockedResult is part of an experimental API and may +// change or be removed. +type PersistenceWriteRawJSONLockedResult struct { + // Why the write failed. Absent when `success` is true. A lock the runtime could not acquire + // in time reports here rather than raising. + Error *string `json:"error,omitempty"` + // Whether the file now holds the requested contents. + Success bool `json:"success"` +} + // Optional message to echo back to the caller. // Experimental: PingRequest is part of an experimental API and may change or be removed. type PingRequest struct { @@ -13261,6 +14498,30 @@ type ProviderModelConfig struct { WireModel *string `json:"wireModel,omitempty"` } +// An authoritative monthly usage reading for the observation's provider, account, and +// service. +// Experimental: ProviderMonthlyUsage is part of an experimental API and may change or be +// removed. +type ProviderMonthlyUsage struct { + // Nonnegative finite consumption reported by the service, preserving zero and fractions. + // Present only when state is available; never computed from tokens or balance differences. + ConsumedQuantity *float64 `json:"consumedQuantity,omitempty"` + // Start of the service-reported monthly billing cycle, in UTC RFC 3339. + CycleStart *string `json:"cycleStart,omitempty"` + // Service read time in UTC RFC 3339, not a ledger reconciliation watermark or confirmation + // of the latest inference charge. + QueriedAt *string `json:"queriedAt,omitempty"` + // End of the service-reported monthly billing cycle, in UTC RFC 3339. + ResetOn *string `json:"resetOn,omitempty"` + // Identity scope of the reading within its provider and service. + Scope ProviderMonthlyUsageScope `json:"scope"` + // Availability of monthly consumption. Missing or unavailable usage must never be + // interpreted as zero. + State ProviderMonthlyUsageState `json:"state"` + // Unit of consumedQuantity; independent of the parent observation's balance unit. + Unit ProviderQuotaUnit `json:"unit"` +} + // Authoritative budget measurements and policy metadata, independent of provider. // Experimental: ProviderQuotaBudgetMetadata is part of an experimental API and may change // or be removed. @@ -13319,6 +14580,9 @@ type ProviderQuotaState struct { HasQuota *bool `json:"hasQuota,omitempty"` // HTTP status from acquisition, when available. HTTPStatus *int64 `json:"httpStatus,omitempty"` + // Service-reported monthly consumption, independent of quota balances and per-call or + // session cost. Omitted when the service does not report monthly usage. + MonthlyUsage *ProviderMonthlyUsage `json:"monthlyUsage,omitempty"` // Whether this is a GET account reading or a pre-response admission observation. // Observations are never merged across kinds. ObservationKind *ProviderQuotaObservationKind `json:"observationKind,omitempty"` @@ -14456,6 +15720,100 @@ type RemoteSessionRepository struct { Owner string `json:"owner"` } +// One atomic edit of an allowed top-level repository setting. +// Experimental: RepoSettingsEditRequest is part of an experimental API and may change or be +// removed. +// Internal: RepoSettingsEditRequest is an internal SDK API and is not part of the public +// surface. +type RepoSettingsEditRequest struct { + // One canonical top-level repository setting that the allowlist permits. + Key string `json:"key"` + // Set a scalar, add or remove a string list item, or remove the setting. The handler + // rejects a missing or incompatible value. + // Internal: Operation is part of the SDK's internal API surface and is not intended for + // external use. + Operation SettingsEditOperation `json:"operation"` + // Absolute path of the repository root. + RepoRoot string `json:"repoRoot"` + // Shared or local repository settings file. + // Internal: Scope is part of the SDK's internal API surface and is not intended for + // external use. + Scope RepoSettingsEditScope `json:"scope"` + // Required boolean, string or number for set, required string for add-item and remove-item, + // and forbidden for remove. The runtime rejects all other JSON values. + Value any `json:"value,omitempty"` +} + +// Which repository settings file to read. +// Experimental: RepoSettingsReadRawRequest is part of an experimental API and may change or +// be removed. +// Internal: RepoSettingsReadRawRequest is an internal SDK API and is not part of the public +// surface. +type RepoSettingsReadRawRequest struct { + // Absolute path of the repository root. + RepoRoot string `json:"repoRoot"` + // Use repo for shared settings or local for settings.local.json. Other scopes are rejected. + Scope string `json:"scope"` +} + +// Parsed repository settings without normalization or layering. +// Experimental: RepoSettingsReadRawResult is part of an experimental API and may change or +// be removed. +// Internal: RepoSettingsReadRawResult is an internal SDK API and is not part of the public +// surface. +type RepoSettingsReadRawResult struct { + // Stored JSON values after the existing JSONC parser and prototype-safety checks. Empty + // when the file is missing, blank, or invalid. + Config any `json:"config"` + // True when the file exists but did not parse, so `config` is empty for that reason rather + // than because nothing is set. + Invalid bool `json:"invalid"` +} + +// The complete set of repository-overridable settings to store. +// Experimental: RepoSettingsWriteAllRequest is part of an experimental API and may change +// or be removed. +// Internal: RepoSettingsWriteAllRequest is an internal SDK API and is not part of the +// public surface. +type RepoSettingsWriteAllRequest struct { + // Absolute path of the repository root. + RepoRoot string `json:"repoRoot"` + // Use repo for shared settings or local for settings.local.json. Other scopes are rejected. + Scope string `json:"scope"` + // Object of allowed settings to store. Missing allowlisted keys are removed. Other input + // keys are ignored, while unknown stored keys are preserved. + Settings any `json:"settings"` +} + +// Experimental: RepoSettingsWriteAllResult is part of an experimental API and may change or +// be removed. +type RepoSettingsWriteAllResult struct { +} + +// One key to write or remove in a repository settings file. +// Experimental: RepoSettingsWriteKeyRequest is part of an experimental API and may change +// or be removed. +// Internal: RepoSettingsWriteKeyRequest is an internal SDK API and is not part of the +// public surface. +type RepoSettingsWriteKeyRequest struct { + // True to remove the key instead of writing it. + Delete *bool `json:"delete,omitempty"` + // Canonical top-level key permitted by the repository settings allowlist. + Key string `json:"key"` + // Absolute path of the repository root. + RepoRoot string `json:"repoRoot"` + // Use repo for shared settings or local for settings.local.json. Other scopes are rejected. + Scope string `json:"scope"` + // Value to validate and write. Required unless delete is true; a null value is validated, + // not treated as deletion. + Value any `json:"value,omitempty"` +} + +// Experimental: RepoSettingsWriteKeyResult is part of an experimental API and may change or +// be removed. +type RepoSettingsWriteKeyResult struct { +} + // Experimental: ResponseFormat is part of an experimental API and may change or be removed. type ResponseFormat struct { // JSON Schema and provider options for the turn's output. @@ -14469,6 +15827,30 @@ type ResponseFormat struct { type RuntimeShutdownResult struct { } +// The stored sandbox settings to resolve against the defaults. +// Experimental: SandboxApplyDefaultsRequest is part of an experimental API and may change +// or be removed. +type SandboxApplyDefaultsRequest struct { + // The sandbox settings as stored in the settings file. An empty object resolves to the + // defaults alone. + Stored any `json:"stored"` +} + +// True when the command stays available while enforcement is blocked. +// Experimental: SandboxCheckEnforcementCommandResult is part of an experimental API and may +// change or be removed. +type SandboxCheckEnforcementCommandResult bool + +// True when an OS-level sandbox can run on this host. +// Experimental: SandboxCheckPlatformSupportResult is part of an experimental API and may +// change or be removed. +type SandboxCheckPlatformSupportResult bool + +// True when the sandbox on this host can reach host loopback directly. +// Experimental: SandboxCheckWindowsHostLoopbackSupportResult is part of an experimental API +// and may change or be removed. +type SandboxCheckWindowsHostLoopbackSupportResult bool + // Resolved sandbox configuration. // Experimental: SandboxConfig is part of an experimental API and may change or be removed. type SandboxConfig struct { @@ -14658,6 +16040,29 @@ type SandboxCredentialsConfig struct { EnvVars map[string]SandboxMaskedEnvVar `json:"envVars"` } +// A possible secret-bearing environment variable and optional locally suggested HTTPS +// injection hosts. This is a draft for user review, not an active grant. +// Experimental: SandboxCredentialSuggestion is part of an experimental API and may change +// or be removed. +type SandboxCredentialSuggestion struct { + // Environment variable name. The secret value is never returned. + Name string `json:"name"` + // HTTPS hostnames suggested by a local known-provider mapping. An empty list requires the + // user to supply injection hosts before adding a masking entry. These suggestions do not + // allow network access. + SuggestedInjectHosts []string `json:"suggestedInjectHosts"` +} + +// Possible unconfigured secrets in the sandbox shell environment, sorted by variable name, +// with no secret values. +// Experimental: SandboxCredentialSuggestionsResult is part of an experimental API and may +// change or be removed. +type SandboxCredentialSuggestionsResult struct { + // Candidates for user review. Empty when sandboxing is disabled or no unconfigured + // candidates are present. + Suggestions []SandboxCredentialSuggestion `json:"suggestions"` +} + // Request to disable sandboxing for the current session while resolving an active // sandbox-bypass permission prompt. // Experimental: SandboxDisableForSessionRequest is part of an experimental API and may @@ -14680,6 +16085,52 @@ type SandboxDisableForSessionResult struct { Success bool `json:"success"` } +// The documentation URL a host cites when it declines or downgrades a sandbox setting. +// Experimental: SandboxDocsURLResult is part of an experimental API and may change or be +// removed. +type SandboxDocsURLResult struct { + // Documentation URL describing the sandbox and the settings that control it. + URL string `json:"url"` +} + +// A sandbox config and the per-feature routing flag to resolve it for. +// Experimental: SandboxEffectiveForOptionalParams is part of an experimental API and may +// change or be removed. +// Internal: SandboxEffectiveForOptionalParams is an internal SDK API and is not part of the +// public surface. +type SandboxEffectiveForOptionalParams struct { + // The routing flag naming the feature being launched, such as `sandboxMcpServers` or + // `sandboxLspServers`. + RoutingFlag string `json:"routingFlag"` + // The session's resolved `sandbox` slice. Omitted when the host has none, which yields a + // disabled sandbox rather than an error. + Sandbox any `json:"sandbox,omitempty"` +} + +// The sandbox a named feature actually launches under. +// Experimental: SandboxEffectiveForOptionalResult is part of an experimental API and may +// change or be removed. +// Internal: SandboxEffectiveForOptionalResult is an internal SDK API and is not part of the +// public surface. +type SandboxEffectiveForOptionalResult struct { + // The effective sandbox config for the requested feature, or a disabled sandbox when the + // feature is routed out of it. + SandboxJSON any `json:"sandboxJson"` +} + +// One slash command, as typed, to test against the enforcement-block allowlist. +// Experimental: SandboxEnforcementCommandAllowedParams is part of an experimental API and +// may change or be removed. +// Internal: SandboxEnforcementCommandAllowedParams is an internal SDK API and is not part +// of the public surface. +type SandboxEnforcementCommandAllowedParams struct { + // The command's arguments, in order. Pass an empty array for a bare command: several + // entries in the allowlist admit the bare form and refuse every argument. + Args []string `json:"args"` + // The command name as the user typed it, with or without its leading slash. + CommandName string `json:"commandName"` +} + // Managed sandbox enforcement state for a session. // Experimental: SandboxEnforcementStatus is part of an experimental API and may change or // be removed. @@ -14692,6 +16143,43 @@ type SandboxEnforcementStatus struct { Required bool `json:"required"` } +// A batch of file-picker candidates to check against a prepared policy. +// Experimental: SandboxFilterPickerCandidatesParams is part of an experimental API and may +// change or be removed. +// Internal: SandboxFilterPickerCandidatesParams is an internal SDK API and is not part of +// the public surface. +type SandboxFilterPickerCandidatesParams struct { + // The candidates to check, in the order the answers are returned. + // Internal: Candidates is part of the SDK's internal API surface and is not intended for + // external use. + Candidates []SandboxPickerCandidate `json:"candidates"` + // The session's working directory, matching the one the policy was prepared from. + Cwd string `json:"cwd"` + // The policy returned by `sandbox.preparePickerPolicy`. Omitted keeps every candidate. + Sandbox any `json:"sandbox,omitempty"` +} + +// Per-candidate allow decisions, positionally aligned with the request. +// Experimental: SandboxFilterPickerCandidatesResult is part of an experimental API and may +// change or be removed. +// Internal: SandboxFilterPickerCandidatesResult is an internal SDK API and is not part of +// the public surface. +type SandboxFilterPickerCandidatesResult struct { + // One decision per candidate, in the same order. Always the same length as the request's + // `candidates`. + Allowed []bool `json:"allowed"` +} + +// The user-facing refusal, including the platform remedy and the documentation link. +// Experimental: SandboxGetUnsupportedMessageResult is part of an experimental API and may +// change or be removed. +type SandboxGetUnsupportedMessageResult string + +// The user-facing notice that a saved sandbox setting is inert for this session. +// Experimental: SandboxGetUnsupportedSettingDisabledNoticeResult is part of an experimental +// API and may change or be removed. +type SandboxGetUnsupportedSettingDisabledNoticeResult string + // Request to accept the sandbox path grant offered on an active sandbox escalation // permission prompt. // Experimental: SandboxGrantPathForRequestRequest is part of an experimental API and may @@ -14795,6 +16283,56 @@ type SandboxMaskedEnvVar struct { InjectHosts []string `json:"injectHosts"` } +// One file-picker suggestion offered up for the sandbox filesystem check. +// Experimental: SandboxPickerCandidate is part of an experimental API and may change or be +// removed. +// Internal: SandboxPickerCandidate is an internal SDK API and is not part of the public +// surface. +type SandboxPickerCandidate struct { + // Whether the suggestion is a directory. A directory the policy does not itself allow may + // still be needed as a waypoint to an allowed subtree. + IsDirectory bool `json:"isDirectory"` + // Absolute path the suggestion would attach. + Path string `json:"path"` +} + +// The working directory and sandbox settings to build a file-picker policy from. +// Experimental: SandboxPreparePickerPolicyParams is part of an experimental API and may +// change or be removed. +// Internal: SandboxPreparePickerPolicyParams is an internal SDK API and is not part of the +// public surface. +type SandboxPreparePickerPolicyParams struct { + // The session's working directory. Git discovery and grant canonicalization are resolved + // against it. + Cwd string `json:"cwd"` + // The session's resolved `sandbox` slice. Omitted, or disabled, yields no policy and every + // candidate is kept. + Sandbox any `json:"sandbox,omitempty"` +} + +// The prepared file-picker policy, or none when the sandbox is disabled. +// Experimental: SandboxPreparePickerPolicyResult is part of an experimental API and may +// change or be removed. +// Internal: SandboxPreparePickerPolicyResult is an internal SDK API and is not part of the +// public surface. +type SandboxPreparePickerPolicyResult struct { + // The prepared policy to pass back to `sandbox.filterPickerCandidates`. Absent when the + // sandbox is disabled or absent, in which case filtering keeps every candidate. + SandboxJSON any `json:"sandboxJson,omitempty"` +} + +// True when this process can offer installing the sandbox credential-proxy certificate +// authority into OS trust. +// Experimental: SandboxProxyCaCheckInstallationSupportResult is part of an experimental API +// and may change or be removed. +type SandboxProxyCaCheckInstallationSupportResult bool + +// True when the sandbox configuration uses the credential proxy and can need +// certificate-authority setup. +// Experimental: SandboxProxyCaCheckRelevanceResult is part of an experimental API and may +// change or be removed. +type SandboxProxyCaCheckRelevanceResult bool + // Result of creating the persistent certificate authority of the sandbox credential proxy. // Experimental: SandboxProxyCaCreateResult is part of an experimental API and may change or // be removed. @@ -14830,6 +16368,108 @@ type SandboxProxyCaStatus struct { State SandboxProxyCaState `json:"state"` } +// Experimental: SandboxProxySecretDeleteResult is part of an experimental API and may +// change or be removed. +type SandboxProxySecretDeleteResult struct { +} + +// The stored sandbox proxy password, if one is present. +// Experimental: SandboxProxySecretGetResult is part of an experimental API and may change +// or be removed. +type SandboxProxySecretGetResult struct { + // The stored password. Absent when no entry exists in either the keychain or the on-disk + // fallback store. + Value *string `json:"value,omitempty"` +} + +// Where this runtime keeps the sandbox proxy password. +// Experimental: SandboxProxySecretLocation is part of an experimental API and may change or +// be removed. +type SandboxProxySecretLocation struct { + // Absolute path of the credential store holding the sandbox proxy password. The proxy + // password belongs to no MCP server, so it is kept in its own directory rather than in the + // MCP secret store. + ConfigDir string `json:"configDir"` + // Identifier of the proxy password entry within that store. Derived from the configuration + // directory, so two Copilot homes on one machine never share an entry. Use it to recognize + // a `${secret:…}` reference in stored settings as this runtime's own proxy password. + SecretID string `json:"secretId"` +} + +// The sandbox proxy password to store. +// Experimental: SandboxProxySecretSaveRequest is part of an experimental API and may change +// or be removed. +type SandboxProxySecretSaveRequest struct { + // The password to store for the sandbox proxy. + Value string `json:"value"` +} + +// Where the sandbox proxy password was stored. +// Experimental: SandboxProxySecretSaveResult is part of an experimental API and may change +// or be removed. +type SandboxProxySecretSaveResult struct { + // Whether the OS keychain accepted the value. False means it was written to the on-disk + // fallback store instead, which is still readable by `sandbox.proxySecret.get`. + StoredInKeychain bool `json:"storedInKeychain"` +} + +// The user's sandbox settings and the managed sandbox floor to raise them to. +// Experimental: SandboxResolveFlooredConfigParams is part of an experimental API and may +// change or be removed. +// Internal: SandboxResolveFlooredConfigParams is an internal SDK API and is not part of the +// public surface. +type SandboxResolveFlooredConfigParams struct { + // The effective managed `sandbox` slice acting as a floor. Omitted when no administrator + // policy applies, in which case the user's own settings stand unchanged. + ManagedSandbox any `json:"managedSandbox,omitempty"` + // The user's own `sandbox` settings slice. Omitted when the user has configured none, which + // still yields the managed floor rather than a disabled sandbox. + UserSandbox any `json:"userSandbox,omitempty"` +} + +// The user's sandbox settings raised to the managed floor. +// Experimental: SandboxResolveFlooredConfigResult is part of an experimental API and may +// change or be removed. +type SandboxResolveFlooredConfigResult struct { + // The floored sandbox config, normalized through the declared sandbox shape so only + // declared fields in a fixed order are present. + SandboxJSON any `json:"sandboxJson"` +} + +// The effective sandbox settings, with every unset value resolved to its default. +// Experimental: SandboxSettingsApplyDefaultsResult is part of an experimental API and may +// change or be removed. +type SandboxSettingsApplyDefaultsResult map[string]any + +// The managed sandbox policy in force, which selects which remedy the notice offers. +// Experimental: SandboxUnsupportedStartupNoticeParams is part of an experimental API and +// may change or be removed. +// Internal: SandboxUnsupportedStartupNoticeParams is an internal SDK API and is not part of +// the public surface. +type SandboxUnsupportedStartupNoticeParams struct { + // True when the sandbox stayed on only because the managed policy could not be determined. + // Distinguishes a policy that positively enforces the sandbox from one that merely failed + // to resolve, which is the difference between telling the user to contact an administrator + // and telling them to wait. Defaults to false. + EnabledByUndeterminedPolicy *bool `json:"enabledByUndeterminedPolicy,omitempty"` + // The managed `sandbox` slice, when a policy supplies one. The notice treats the sandbox as + // enforced only when this both enables it and sets `failIfUnavailable`, because a managed + // sandbox that tolerates being unavailable still leaves the user able to turn it off. + ManagedSandbox any `json:"managedSandbox,omitempty"` +} + +// The startup warning, absent when the machine can run a sandbox. +// Experimental: SandboxUnsupportedStartupNoticeResult is part of an experimental API and +// may change or be removed. +// Internal: SandboxUnsupportedStartupNoticeResult is an internal SDK API and is not part of +// the public surface. +type SandboxUnsupportedStartupNoticeResult struct { + // The user-facing warning, carrying the platform remedy and the recovery the policy in + // force actually allows. Absent when this machine supports a sandbox, so a host can call + // unconditionally and print only what it gets. + Notice *string `json:"notice,omitempty"` +} + // Register an absolute-time scheduled prompt. // Experimental: ScheduleAddAtRequest is part of an experimental API and may change or be // removed. @@ -16540,6 +18180,12 @@ type SessionOpenOptions struct { AdditionalDirectories []string `json:"additionalDirectories,omitzero"` // Runtime context discriminator for agent filtering. AgentContext *string `json:"agentContext,omitempty"` + // Explicit discovery-only agent roots, separate from customAgents and filesystem grants. + // Available without enableConfigDiscovery, subject to managed/source disables. Relative SDK + // paths resolve once when supplied against the request's effective workingDirectory; + // retained roots do not move on cwd changes. Null and omission are equivalent; [] clears + // only the explicit SDK contribution. + AgentDirectories []string `json:"agentDirectories,omitzero"` // Whether to include instructions from every MCP server in the system prompt instead of // only allowlisted servers. AllowAllMCPServerInstructions *bool `json:"allowAllMcpServerInstructions,omitempty"` @@ -16617,8 +18263,8 @@ type SessionOpenOptions struct { EnableOnDemandInstructionDiscovery *bool `json:"enableOnDemandInstructionDiscovery,omitempty"` // Whether shell-script safety heuristics are enabled. EnableScriptSafety *bool `json:"enableScriptSafety,omitempty"` - // Whether skill loading is enabled. When omitted, an SDK skill provider enables skills by - // default. + // Whether skill loading is enabled. When omitted, explicit SDK skill roots or an SDK skill + // provider enable skills independently of enableConfigDiscovery. EnableSkills *bool `json:"enableSkills,omitempty"` // Whether model responses stream as delta events. EnableStreaming *bool `json:"enableStreaming,omitempty"` @@ -16657,6 +18303,13 @@ type SessionOpenOptions struct { // Internal: HasSkillProvider is part of the SDK's internal API surface and is not intended // for external use. HasSkillProvider *bool `json:"hasSkillProvider,omitempty"` + // Explicit discovery-only file-hook roots, separate from callback hooks and filesystem + // grants. Available without enableConfigDiscovery or enableFileHooks, which controls + // repository-origin hooks only; hook/managed disables still apply. Relative SDK paths + // resolve once when supplied against the request's effective workingDirectory; retained + // roots do not move on cwd changes. Null and omission are equivalent; [] clears only the + // explicit SDK contribution. + HookDirectories []string `json:"hookDirectories,omitzero"` // Skill scan directories and descendants excluded from discovery. Supports `~`-relative // paths. IgnoredSkillsLocations []string `json:"ignoredSkillsLocations,omitzero"` @@ -16670,6 +18323,12 @@ type SessionOpenOptions struct { IncludedBuiltinSkills []string `json:"includedBuiltinSkills,omitzero"` // Installed plugins visible to the session. InstalledPlugins []InstalledPlugin `json:"installedPlugins,omitzero"` + // Explicit discovery-only instruction roots using existing SDK instruction formats, not + // filesystem grants. Available without enableConfigDiscovery, subject to + // instruction/managed disables. Relative SDK paths resolve once when supplied against the + // request's effective workingDirectory; retained roots do not move on cwd changes. Null and + // omission are equivalent; [] clears only the explicit SDK contribution. + InstructionDirectories []string `json:"instructionDirectories,omitzero"` // Stable integration identifier for analytics. IntegrationID *string `json:"integrationId,omitempty"` // Whether experimental behavior is enabled. @@ -16747,7 +18406,11 @@ type SessionOpenOptions struct { ShellInitProfile *string `json:"shellInitProfile,omitempty"` // PowerShell process flags applied to built-in and user-requested shell commands. ShellProcessFlags []string `json:"shellProcessFlags,omitzero"` - // Additional directories to search for skills. + // Explicit discovery-only skill roots, not filesystem grants. Available without + // enableConfigDiscovery, subject to skill/managed disables. Relative SDK paths resolve once + // when supplied against the request's effective workingDirectory; retained roots do not + // move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK + // contribution. SkillDirectories []string `json:"skillDirectories,omitzero"` // Whether to skip custom instruction sources. SkipCustomInstructions *bool `json:"skipCustomInstructions,omitempty"` @@ -17067,6 +18730,23 @@ type SessionPluginsReloadRequest struct { type SessionPluginsReloadResult struct { } +// Experimental: SessionPluginsRetryManagedRequest is part of an experimental API and may +// change or be removed. +type SessionPluginsRetryManagedRequest struct { + // "plugin@marketplace" specs to retry. Omit to retry every plugin the session's managed + // settings require. + Plugins []string `json:"plugins,omitzero"` +} + +// Per-plugin outcomes of retrying the plugins required by the session's managed settings. +// Experimental: SessionPluginsRetryManagedResult is part of an experimental API and may +// change or be removed. +type SessionPluginsRetryManagedResult struct { + // One entry per requested plugin, in request order. When no plugins were requested, one + // entry per plugin the managed settings require. + Plugins []ManagedPluginRetryEntry `json:"plugins"` +} + // Experimental: SessionPluginsUninstallResult is part of an experimental API and may change // or be removed. type SessionPluginsUninstallResult struct { @@ -18080,6 +19760,12 @@ type SessionUpdateOptionsParams struct { AdditionalContentExclusionPolicies []OptionsUpdateAdditionalContentExclusionPolicy `json:"additionalContentExclusionPolicies,omitzero"` // Runtime context discriminator (e.g., `cli`, `actions`). AgentContext *string `json:"agentContext,omitempty"` + // Explicit discovery-only agent roots, separate from customAgents and filesystem grants. + // Available without enableConfigDiscovery, subject to managed/source disables. Relative SDK + // paths resolve once when supplied against the request's effective workingDirectory; + // retained roots do not move on cwd changes. Null and omission are equivalent; [] clears + // only the explicit SDK contribution. + AgentDirectories []string `json:"agentDirectories,omitzero"` // Whether to include instructions from every MCP server in the system prompt instead of // only allowlisted servers. AllowAllMCPServerInstructions *bool `json:"allowAllMcpServerInstructions,omitempty"` @@ -18107,8 +19793,9 @@ type SessionUpdateOptionsParams struct { DisabledInstructionSources []string `json:"disabledInstructionSources,omitzero"` // Skill IDs that should be excluded from this session. DisabledSkills []string `json:"disabledSkills,omitzero"` - // Whether to enable loading of `.github/hooks/` filesystem hooks. Separate from the SDK - // callback hook mechanism. + // Whether to enable repository-origin file hooks from `.github/hooks/` and trusted + // repository-configured roots. Does not control explicit hookDirectories or SDK callback + // hooks. EnableFileHooks *bool `json:"enableFileHooks,omitempty"` // Whether to enable host git operations (context resolution, child repo scanning, git info // in system prompt). @@ -18123,9 +19810,10 @@ type SessionUpdateOptionsParams struct { EnableScriptSafety *bool `json:"enableScriptSafety,omitempty"` // Whether to enable cross-session store writes and reads. EnableSessionStore *bool `json:"enableSessionStore,omitempty"` - // Whether skill loading is enabled. Explicit false disables every source, including a bound - // SDK provider; changing the value invalidates the loaded skill snapshot. When omitted, - // creation falls back to enableConfigDiscovery unless an SDK skill provider is registered. + // Whether skill loading is enabled. Explicit false disables every source, including + // explicit SDK roots and a bound SDK provider; changing the value invalidates the loaded + // skill snapshot. When omitted, creation falls back to enableConfigDiscovery unless + // explicit SDK skill roots or an SDK skill provider are supplied. EnableSkills *bool `json:"enableSkills,omitempty"` // Whether to stream model responses. EnableStreaming *bool `json:"enableStreaming,omitempty"` @@ -18145,6 +19833,13 @@ type SessionUpdateOptionsParams struct { ExcludedTools []string `json:"excludedTools,omitzero"` // Map of feature-flag IDs to their boolean enabled state. FeatureFlags map[string]bool `json:"featureFlags,omitzero"` + // Explicit discovery-only file-hook roots, separate from callback hooks and filesystem + // grants. Available without enableConfigDiscovery or enableFileHooks, which controls + // repository-origin hooks only; hook/managed disables still apply. Relative SDK paths + // resolve once when supplied against the request's effective workingDirectory; retained + // roots do not move on cwd changes. Null and omission are equivalent; [] clears only the + // explicit SDK contribution. + HookDirectories []string `json:"hookDirectories,omitzero"` // Skill scan directories and descendants excluded from discovery. Supports `~`-relative // paths. IgnoredSkillsLocations []string `json:"ignoredSkillsLocations,omitzero"` @@ -18159,6 +19854,12 @@ type SessionUpdateOptionsParams struct { // Full set of installed plugins for the session. Replaces the existing list; the runtime // invalidates the skills cache only when the list materially changes. InstalledPlugins []SessionInstalledPlugin `json:"installedPlugins,omitzero"` + // Explicit discovery-only instruction roots using existing SDK instruction formats, not + // filesystem grants. Available without enableConfigDiscovery, subject to + // instruction/managed disables. Relative SDK paths resolve once when supplied against the + // request's effective workingDirectory; retained roots do not move on cwd changes. Null and + // omission are equivalent; [] clears only the explicit SDK contribution. + InstructionDirectories []string `json:"instructionDirectories,omitzero"` // Stable integration identifier used for analytics and rate-limit attribution. IntegrationID *string `json:"integrationId,omitempty"` // Whether experimental capabilities are enabled. @@ -18214,7 +19915,11 @@ type SessionUpdateOptionsParams struct { ShellInitProfile *string `json:"shellInitProfile,omitempty"` // PowerShell process flags applied to built-in and user-requested shell commands. ShellProcessFlags []string `json:"shellProcessFlags,omitzero"` - // Additional directories to search for skills. + // Explicit discovery-only skill roots, not filesystem grants. Available without + // enableConfigDiscovery, subject to skill/managed disables. Relative SDK paths resolve once + // when supplied against the request's effective workingDirectory; retained roots do not + // move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK + // contribution. SkillDirectories []string `json:"skillDirectories,omitzero"` // Whether to skip loading custom instruction sources. SkipCustomInstructions *bool `json:"skipCustomInstructions,omitempty"` @@ -18405,6 +20110,18 @@ func (UserAuthInfo) settableAuthInfoType() SettableAuthInfoType { return SettableAuthInfoTypeUser } +// Outcome of an atomic user or repository settings edit. +// Experimental: SettingsEditResult is part of an experimental API and may change or be +// removed. +// Internal: SettingsEditResult is an internal SDK API and is not part of the public surface. +type SettingsEditResult struct { + // Changed, unchanged or invalid existing file. Rejected input, validation failures and I/O + // errors remain RPC errors. + // Internal: Status is part of the SDK's internal API surface and is not intended for + // external use. + Status SettingsEditStatus `json:"status"` +} + // User-requested shell execution cancellation handle. // Experimental: ShellCancelUserRequestedRequest is part of an experimental API and may // change or be removed. @@ -19358,7 +21075,10 @@ type SkillsDiscoverRequest struct { IgnoredSkillsLocations []string `json:"ignoredSkillsLocations,omitzero"` // Optional list of project directory paths to scan for project-scoped skills ProjectPaths []string `json:"projectPaths,omitzero"` - // Optional list of additional skill directory paths to include + // Explicit discovery-only skill roots, not filesystem grants. Available without ambient + // discovery, subject to skill/managed disables. Relative paths resolve against the + // request's effective working directory when supplied. Null and omission are equivalent; an + // empty array contributes no explicit roots. SkillDirectories []string `json:"skillDirectories,omitzero"` } @@ -19396,6 +21116,11 @@ type SkillsGetDiscoveryPathsRequest struct { // Optional list of project directory paths. When omitted or empty, only personal and custom // directories are returned. ProjectPaths []string `json:"projectPaths,omitzero"` + // Explicit discovery-only skill roots to enumerate, including configured missing + // directories. Relative paths resolve against the request's effective working directory + // when supplied. These are not filesystem grants or preferred creation targets. Null and + // omission are equivalent. + SkillDirectories []string `json:"skillDirectories,omitzero"` } // Skills invoked during this session, ordered by invocation time (most recent last). @@ -21274,6 +22999,113 @@ type UserSettingMetadata struct { Value any `json:"value"` } +// A single top-level setting subtree to validate and write. +// Experimental: UserSettingsApplyTopKeyRequest is part of an experimental API and may +// change or be removed. +type UserSettingsApplyTopKeyRequest struct { + // Canonical path of the setting the caller is editing. Currently informational only. + CanonicalPath []string `json:"canonicalPath"` + // Records that the user chose an update channel explicitly, so the one-time staff channel + // migration stops reapplying. Honored only for the `autoUpdatesChannel` key. + CompleteStaffUpdateChannelMigration *bool `json:"completeStaffUpdateChannelMigration,omitempty"` + // True to remove the top-level key instead of writing it. + Delete *bool `json:"delete,omitempty"` + // Top-level setting name to write. + TopKey string `json:"topKey"` + // Value to write for the whole top-level key. Omitted when `delete` is true. Explicit null + // is preserved and validated as null. + Value *any `json:"value,omitempty"` +} + +// Outcome of validating and writing one top-level setting subtree. +// Experimental: UserSettingsApplyTopKeyResult is part of an experimental API and may change +// or be removed. +type UserSettingsApplyTopKeyResult struct { + // Why the write failed, present only when `status` is `write-error`. + Error *string `json:"error,omitempty"` + // Problems found, present only when `status` is `validation-error`. + Issues []UserSettingsValidationIssue `json:"issues,omitzero"` + // `ok` when the value was written, `validation-error` when it was rejected before any + // write, `write-error` when validation passed but the write failed. + Status string `json:"status"` +} + +// The overrides and host context to resolve effective runtime settings from. +// Experimental: UserSettingsBuildRuntimeRequest is part of an experimental API and may +// change or be removed. +// Internal: UserSettingsBuildRuntimeRequest is an internal SDK API and is not part of the +// public surface. +type UserSettingsBuildRuntimeRequest struct { + // The environment to read settings-bearing variables from. Passed explicitly rather than + // read from the server process so a host can resolve settings for an environment it is + // about to create rather than the one it runs in. + Environment map[string]string `json:"environment"` + // Settings that take precedence over every document and environment layer, such as the + // values a host derived from its own command line. Keyed by setting name, with each value + // carried verbatim, so a host may pass keys this runtime does not recognize. Absent when + // the host contributes no overrides of its own. + Overrides map[string]any `json:"overrides,omitzero"` + // The platform to apply platform-specific defaults for, in the same spelling the host's own + // runtime reports. + Platform string `json:"platform"` + // The directory the settings are being resolved for. This selects the repository-scoped + // document, so a host resolving settings for a directory other than its own process working + // directory must pass that directory here. + WorkingDirectory string `json:"workingDirectory"` +} + +// The effective runtime settings, keyed by setting name. Keys this runtime does not +// recognize are preserved rather than dropped, so a caller reading a document written by a +// newer runtime keeps every value. +// Experimental: UserSettingsBuildRuntimeResult is part of an experimental API and may +// change or be removed. +type UserSettingsBuildRuntimeResult map[string]any + +// One atomic edit of a canonical setting in settings.json. +// Experimental: UserSettingsEditRequest is part of an experimental API and may change or be +// removed. +// Internal: UserSettingsEditRequest is an internal SDK API and is not part of the public +// surface. +type UserSettingsEditRequest struct { + // Nonempty canonical path segments of the setting to edit, starting with its top-level key. + CanonicalPath []string `json:"canonicalPath"` + // Configuration directory to edit. Omitted or empty resolves the server's own home, as in + // `user.settings.loadForConfigDir`. + ConfigDir *string `json:"configDir,omitempty"` + // Set a scalar, add or remove a string list item, or remove the setting. The handler + // rejects a missing or incompatible value. + // Internal: Operation is part of the SDK's internal API surface and is not intended for + // external use. + Operation SettingsEditOperation `json:"operation"` + // Required boolean, string or number for set, required string for add-item and remove-item, + // and forbidden for remove. The runtime rejects all other JSON values. + Value any `json:"value,omitempty"` +} + +// Selects which settings file paths to resolve. +// Experimental: UserSettingsGetPathsRequest is part of an experimental API and may change +// or be removed. +type UserSettingsGetPathsRequest struct { + // Absolute path of a repository root. Supply it to also resolve that repository's two + // settings file paths; omit it to resolve only the user path. + RepoRoot *string `json:"repoRoot,omitempty"` +} + +// On-disk locations of the settings files this runtime reads. A path is reported whether or +// not the file exists. +// Experimental: UserSettingsGetPathsResult is part of an experimental API and may change or +// be removed. +type UserSettingsGetPathsResult struct { + // The repository's local, untracked settings file, or null when no repository root was + // supplied. + RepoLocalSettings *string `json:"repoLocalSettings,omitempty"` + // The repository's checked-in settings file, or null when no repository root was supplied. + RepoSettings *string `json:"repoSettings,omitempty"` + // The user settings file: `settings.json` under the resolved Copilot home. This is the file + // `user.settings.get`, `user.settings.load`, and `user.settings.set` read and write. + UserSettings string `json:"userSettings"` +} + // Per-key metadata for every known user setting in settings.json, including settings left // at their default. Excludes repository- and enterprise-managed overrides. // Experimental: UserSettingsGetResult is part of an experimental API and may change or be @@ -21284,6 +23116,418 @@ type UserSettingsGetResult struct { Settings map[string]UserSettingMetadata `json:"settings"` } +// Top-level user-setting keys grouped by who may govern them. +// Experimental: UserSettingsGovernanceKeysResult is part of an experimental API and may +// change or be removed. +type UserSettingsGovernanceKeysResult struct { + // Top-level keys a repository may override through a Claude-compatible repository config. + ClaudeRepo []string `json:"claudeRepo"` + // Top-level keys an enterprise policy may fix through managed settings. + Managed []string `json:"managed"` + // Top-level keys a repository may override through `.github/copilot/settings.json` or + // `.github/copilot/settings.local.json`. + Repo []string `json:"repo"` +} + +// The filesystem and environment facts a user-settings file is resolved from. Passed +// explicitly rather than read from the runtime's own process, so a host whose configuration +// directory differs from its process environment resolves the file it means. +// Experimental: UserSettingsHostContext is part of an experimental API and may change or be +// removed. +type UserSettingsHostContext struct { + // The Copilot configuration directory, when the host set one. Takes precedence over + // `COPILOT_HOME` and the home directory. + ConfigDir *string `json:"configDir,omitempty"` + // The host's environment variables. `COPILOT_HOME` is read from here, as are the variables + // a settings hook may expand. + Environment any `json:"environment"` + // The user's home directory, used when neither a configuration directory nor `COPILOT_HOME` + // is set. + HomeDirectory string `json:"homeDirectory"` +} + +// A repository to read settings from, and the user layer they are merged over. +// Experimental: UserSettingsLoadAndMergeRepoRequest is part of an experimental API and may +// change or be removed. +// Internal: UserSettingsLoadAndMergeRepoRequest is an internal SDK API and is not part of +// the public surface. +type UserSettingsLoadAndMergeRepoRequest struct { + // Absolute path of the repository root whose settings to read. When this is the user's home + // directory, the Claude-compatible configuration is skipped, so a home-directory session + // does not inherit a personal config as if it were repository policy. + RepoRoot string `json:"repoRoot"` + // The user's own settings, keyed by setting name. The repository layer is merged over this; + // a repository may only add to the collection settings it is allowed to influence, so a + // user setting it does not name survives unchanged. + UserSettings map[string]any `json:"userSettings"` +} + +// The settings a consumer should act on inside a repository, together with any diagnostic +// raised while reading the repository layer. +// Experimental: UserSettingsLoadAndMergeRepoResult is part of an experimental API and may +// change or be removed. +// Internal: UserSettingsLoadAndMergeRepoResult is an internal SDK API and is not part of +// the public surface. +type UserSettingsLoadAndMergeRepoResult struct { + // The merged settings document, keyed by setting name. Equals the supplied user layer when + // the repository declares no settings. + Settings map[string]any `json:"settings"` + // Why part of the repository document was ignored, as a single human-readable string + // joining every diagnostic with `; `. Null when it loaded cleanly. The settings that did + // load are still merged, so a consumer should surface this and continue rather than treat + // it as a failure. + Warning *string `json:"warning,omitempty"` +} + +// Selects the configuration directory whose user settings to read. +// Experimental: UserSettingsLoadForConfigDirRequest is part of an experimental API and may +// change or be removed. +// Internal: UserSettingsLoadForConfigDirRequest is an internal SDK API and is not part of +// the public surface. +type UserSettingsLoadForConfigDirRequest struct { + // Copilot configuration directory to read the settings document from, taking precedence + // over the server's own `COPILOT_HOME` and default home. Omit it, or pass an empty string, + // to read the directory the server resolved for itself. + ConfigDir *string `json:"configDir,omitempty"` +} + +// The stored user settings document, keyed by setting name. +// Experimental: UserSettingsLoadForConfigDirResult is part of an experimental API and may +// change or be removed. +type UserSettingsLoadForConfigDirResult map[string]any + +// Contents of settings.json as stored, for a caller about to rewrite it. +// Experimental: UserSettingsLoadForEditResult is part of an experimental API and may change +// or be removed. +type UserSettingsLoadForEditResult struct { + // Parsed contents, present only when `status` is `ok`. + Settings any `json:"settings,omitempty"` + // `ok` when the file parsed, `missing` when there is no file yet, `invalid` when the file + // exists but will not parse. + Status string `json:"status"` +} + +// Selects the repository whose checked-in settings to read. +// Experimental: UserSettingsLoadRepoRequest is part of an experimental API and may change +// or be removed. +type UserSettingsLoadRepoRequest struct { + // Absolute path of the repository root whose settings to read. When this is the user's home + // directory, the Claude-compatible configuration is skipped, so a home-directory session + // does not inherit a personal config as if it were repository policy. + RepoRoot string `json:"repoRoot"` +} + +// A repository's settings as stored, together with any diagnostic raised while reading them. +// Experimental: UserSettingsLoadRepoResult is part of an experimental API and may change or +// be removed. +type UserSettingsLoadRepoResult struct { + // The repository's settings document, keyed by setting name, with each value carried + // verbatim. Null when the repository declares no settings. + Settings map[string]any `json:"settings,omitzero"` + // Why part of the document was ignored, as a single human-readable string joining every + // diagnostic with `; `. Null when the document loaded cleanly. The settings that did load + // are still reported, so a consumer should surface this and continue rather than treat it + // as a failure. + Warning *string `json:"warning,omitempty"` +} + +// The user's stored settings, together with any diagnostic raised while reading them. +// Experimental: UserSettingsLoadWithWarningResult is part of an experimental API and may +// change or be removed. +type UserSettingsLoadWithWarningResult struct { + // The stored user settings document, keyed by setting name, with each value carried + // verbatim. Empty when the file could not be read at all. + Settings map[string]any `json:"settings"` + // Why part of the document was ignored, phrased for display to the user. Null when the + // document loaded cleanly. The settings that did load are still reported, so a consumer may + // surface this and continue rather than treat it as a failure. + Warning *string `json:"warning,omitempty"` +} + +// The managed (enterprise policy) layer and the user layer to fold together. The field +// names carry the layer identity: nothing about a settings object identifies which layer +// produced it, so a generic base/override pair would let a caller transpose them and +// silently resolve the governance keys in the user's favour. +// Experimental: UserSettingsMergeManagedAndUserParams is part of an experimental API and +// may change or be removed. +// Internal: UserSettingsMergeManagedAndUserParams is an internal SDK API and is not part of +// the public surface. +type UserSettingsMergeManagedAndUserParams struct { + // The managed layer. Its governance keys win, and the per-entry `enabledPlugins` and + // `extraKnownMarketplaces` control maps are stamped back from it after the union. Omitted + // when the host is unmanaged. + Managed any `json:"managed,omitempty"` + // The user's own settings layer. Wins on every key the managed layer does not govern. + // Omitted when the user has no settings yet. + User any `json:"user,omitempty"` +} + +// The user layer folded over the managed layer. +// Experimental: UserSettingsMergeManagedAndUserResult is part of an experimental API and +// may change or be removed. +type UserSettingsMergeManagedAndUserResult struct { + // The merged settings. An empty object when neither layer was supplied, so an unmanaged + // host with no user settings governs nothing. + SettingsJSON any `json:"settingsJson"` +} + +// The user layer and the repository layer to fold together. +// Experimental: UserSettingsMergeRepoValueParams is part of an experimental API and may +// change or be removed. +// Internal: UserSettingsMergeRepoValueParams is an internal SDK API and is not part of the +// public surface. +type UserSettingsMergeRepoValueParams struct { + // The repository's checked-in settings layer. + RepoSettings any `json:"repoSettings"` + // The user's settings, already resolved against any managed layer. + UserSettings any `json:"userSettings"` +} + +// The repository layer folded into the user layer. +// Experimental: UserSettingsMergeRepoValueResult is part of an experimental API and may +// change or be removed. +type UserSettingsMergeRepoValueResult struct { + // The merged settings, with the per-entry managed control maps re-stamped from the + // runtime's ambient managed policy. + SettingsJSON any `json:"settingsJson"` +} + +// Selects the configuration directory whose settings document to clean. +// Experimental: UserSettingsMigrateObsoleteOnDiskRequest is part of an experimental API and +// may change or be removed. +// Internal: UserSettingsMigrateObsoleteOnDiskRequest is an internal SDK API and is not part +// of the public surface. +type UserSettingsMigrateObsoleteOnDiskRequest struct { + // The configuration directory holding the settings document to clean. A host that scopes a + // session to its own Copilot home passes that directory here; omit it to clean the document + // under the home the server resolved for itself. The home directory and environment are + // deliberately not caller-supplied: they decide only the settings path root and the two + // hook-parsing flags, and those describe the process doing the reading rather than the + // caller. + ConfigDir *string `json:"configDir,omitempty"` +} + +// The outcome of the one-time obsolete-settings cleanup. +// Experimental: UserSettingsMigrateObsoleteOnDiskResult is part of an experimental API and +// may change or be removed. +// Internal: UserSettingsMigrateObsoleteOnDiskResult is an internal SDK API and is not part +// of the public surface. +type UserSettingsMigrateObsoleteOnDiskResult struct { + // What the cleanup did. Only `changed` rewrote the document. The cleaned settings are not + // returned here: the document is written before this replies, so `user.settings.load` reads + // it authoritatively. + // Internal: Status is part of the SDK's internal API surface and is not intended for + // external use. + Status UserSettingsMigrateObsoleteOnDiskStatus `json:"status"` +} + +// Selects the configuration directory to run the one-time staff log-level migration +// against, and supplies the caller's record of whether it has already run. +// Experimental: UserSettingsMigrateStaffLogLevelOnceRequest is part of an experimental API +// and may change or be removed. +// Internal: UserSettingsMigrateStaffLogLevelOnceRequest is an internal SDK API and is not +// part of the public surface. +type UserSettingsMigrateStaffLogLevelOnceRequest struct { + // When the caller last recorded this migration, as an ISO-8601 timestamp. Supplying it lets + // the runtime answer `current` without re-running the evaluation. + CachedCompletedAt *string `json:"cachedCompletedAt,omitempty"` + // The configuration directory, home directory and environment to resolve the settings and + // global-state documents from. A host that scopes a session to its own Copilot home must + // pass that directory here, or the migration is evaluated against the server's own home + // instead. + Context UserSettingsHostContext `json:"context"` +} + +// The outcome of the one-time staff log-level migration. +// Experimental: UserSettingsMigrateStaffLogLevelOnceResult is part of an experimental API +// and may change or be removed. +// Internal: UserSettingsMigrateStaffLogLevelOnceResult is an internal SDK API and is not +// part of the public surface. +type UserSettingsMigrateStaffLogLevelOnceResult struct { + // When the migration was recorded as done, as an ISO-8601 timestamp. Absent only for + // `ineligible`, which records nothing. + CompletedAt *string `json:"completedAt,omitempty"` + // The settings document as it stands after the migration, keyed by setting name. Present + // only for `refreshed`, and only when the document could be re-read cleanly. + Settings any `json:"settings,omitempty"` + // What the migration did. Only `refreshed` can have changed the persisted settings. + Status StaffLogLevelMigrationStatus `json:"status"` +} + +// The outcome of the one-time staff update-channel migration. +// Experimental: UserSettingsMigrateStaffUpdateChannelOnceResult is part of an experimental +// API and may change or be removed. +// Internal: UserSettingsMigrateStaffUpdateChannelOnceResult is an internal SDK API and is +// not part of the public surface. +type UserSettingsMigrateStaffUpdateChannelOnceResult struct { + // When the migration was recorded as done, as an ISO-8601 timestamp. Absent only for + // `ineligible`, which records nothing. + CompletedAt *string `json:"completedAt,omitempty"` + // What the migration did. Only `migrated` means the persisted settings changed. + Status StaffUpdateChannelMigrationStatus `json:"status"` +} + +// Settings object whose key names should be canonicalized. +// Experimental: UserSettingsNormalizeRequest is part of an experimental API and may change +// or be removed. +type UserSettingsNormalizeRequest struct { + // Free-form settings object, possibly using legacy or snake_case key names. + Settings any `json:"settings"` +} + +// Settings object with canonical key names. +// Experimental: UserSettingsNormalizeResult is part of an experimental API and may change +// or be removed. +type UserSettingsNormalizeResult struct { + // The same settings with every legacy or snake_case key renamed to its canonical spelling. + Settings any `json:"settings"` +} + +// The on-disk location of the user's settings document. +// Experimental: UserSettingsPathResult is part of an experimental API and may change or be +// removed. +type UserSettingsPathResult struct { + // Absolute path to the settings file this runtime reads and writes. Normally + // `settings.json` under the Copilot configuration directory; an extensionless legacy + // `settings` file is reported instead when it exists and `settings.json` does not. + Path string `json:"path"` +} + +// One setting that will not take effect, and why. +// Experimental: UserSettingsProblem is part of an experimental API and may change or be +// removed. +type UserSettingsProblem struct { + // Coarse grouping for the problem, for a caller that presents problems in sections. + Category string `json:"category"` + // Why the setting will not take effect. Never contains the setting's value, which may be a + // token or another secret. + Explanation string `json:"explanation"` + // Stable identifier for this problem within the reported set, so a caller can track a + // selection across refreshes. + ID string `json:"id"` + // What is wrong in machine-readable form, for a caller that wants to branch rather than + // display. + Kind string `json:"kind"` + // The offending key as written in the file, when the problem is attributable to a single + // key. Absent when the whole file is at fault. + RawKey *string `json:"rawKey,omitempty"` + // The settings layer the problem was found in. + Source UserSettingsProblemLocation `json:"source"` +} + +// The settings layer a problem was found in. +// Experimental: UserSettingsProblemLocation is part of an experimental API and may change +// or be removed. +type UserSettingsProblemLocation struct { + // Absolute path of the file backing this layer. + FilePath string `json:"filePath"` + // Which layer this is: `user`, `repo`, or `local`. + Kind string `json:"kind"` + // The caller-supplied display label for the layer. + Label string `json:"label"` +} + +// One already-loaded settings layer to diagnose. +// Experimental: UserSettingsProblemSource is part of an experimental API and may change or +// be removed. +type UserSettingsProblemSource struct { + // The layer's settings exactly as loaded, unnormalized. Empty when the file is missing or + // failed to parse. + Config map[string]any `json:"config,omitzero"` + // Absolute path of the file backing this layer. + FilePath string `json:"filePath"` + // True when the file itself failed to parse, so its keys could not be read at all. + Invalid *bool `json:"invalid,omitempty"` + // Which layer this is: `user`, `repo`, or `local`. + Kind string `json:"kind"` + // Caller-chosen display label for the layer, echoed back on each problem so a caller can + // group by layer without re-deriving its own naming. + Label string `json:"label"` +} + +// The settings layers to diagnose. +// Experimental: UserSettingsProblemsRequest is part of an experimental API and may change +// or be removed. +type UserSettingsProblemsRequest struct { + // Layers to diagnose, in the precedence order the caller applies them. + Sources []UserSettingsProblemSource `json:"sources,omitzero"` +} + +// The settings that will not take effect across the given layers. +// Experimental: UserSettingsProblemsResult is part of an experimental API and may change or +// be removed. +type UserSettingsProblemsResult struct { + // Each setting that will not take effect, and why. Empty when every layer is healthy. + Problems []UserSettingsProblem `json:"problems,omitzero"` +} + +// Selects the configuration directory to run the one-time staff model-selection reset +// against, and supplies the caller's record of whether it has already run. +// Experimental: UserSettingsResetStaffModelSelectionOnceRequest is part of an experimental +// API and may change or be removed. +// Internal: UserSettingsResetStaffModelSelectionOnceRequest is an internal SDK API and is +// not part of the public surface. +type UserSettingsResetStaffModelSelectionOnceRequest struct { + // The configuration directory, home directory and environment to resolve the settings and + // global-state documents from. A host that scopes a session to its own Copilot home must + // pass that directory here, or the reset is evaluated against the server's own home instead. + Context UserSettingsHostContext `json:"context"` + // Whether the signed-in account is a staff account. The reset applies only to staff, so + // anything other than `true` returns `didReset: false` without touching the settings + // document. + Staff *bool `json:"staff,omitempty"` + // When the caller last recorded this reset, as an ISO-8601 timestamp. A timestamp at or + // after the cutoff means the reset already ran and is returned unchanged. + StaffModelResetAt *string `json:"staffModelResetAt,omitempty"` +} + +// The outcome of the one-time staff model-selection reset. +// Experimental: UserSettingsResetStaffModelSelectionOnceResult is part of an experimental +// API and may change or be removed. +// Internal: UserSettingsResetStaffModelSelectionOnceResult is an internal SDK API and is +// not part of the public surface. +type UserSettingsResetStaffModelSelectionOnceResult struct { + // The cutoff this reset is gated on, as an ISO-8601 timestamp. A recorded timestamp at or + // after it means the reset is already done. + Cutoff string `json:"cutoff"` + // Whether the persisted model selection was cleared. Only `true` means the settings + // document changed and the host should adopt the returned settings. + DidReset bool `json:"didReset"` + // The timestamp the host should now record, as an ISO-8601 timestamp. Carries the caller's + // own value back unchanged when nothing was reset. + ResetAt *string `json:"resetAt,omitempty"` + // The settings document as it stands after the reset, keyed by setting name. Present only + // when `didReset` is true. + Settings any `json:"settings,omitempty"` +} + +// One hook to disable or re-enable. +// Experimental: UserSettingsSetHookDisabledRequest is part of an experimental API and may +// change or be removed. +// Internal: UserSettingsSetHookDisabledRequest is an internal SDK API and is not part of +// the public surface. +type UserSettingsSetHookDisabledRequest struct { + // True adds the key to `disabledHooks`, false removes it. + Disabled bool `json:"disabled"` + // Durable content-hash key of the hook, as `hooks.loadResources` reports it. + DisableKey string `json:"disableKey"` + // Path of the settings.json to rewrite. + SettingsPath string `json:"settingsPath"` +} + +// Outcome of rewriting the `disabledHooks` setting. +// Experimental: UserSettingsSetHookDisabledResult is part of an experimental API and may +// change or be removed. +// Internal: UserSettingsSetHookDisabledResult is an internal SDK API and is not part of the +// public surface. +type UserSettingsSetHookDisabledResult struct { + // Why the write failed. Absent when the status is `ok`. + ErrorMessage *string `json:"errorMessage,omitempty"` + // `ok` when the setting was written, `invalid` when settings.json could not be parsed and + // was left alone, `error` when the write itself failed. + Status string `json:"status"` +} + // Partial user settings to write to settings.json. Each top-level key is written // individually, replacing the existing value; a key whose value is null is removed. // Experimental: UserSettingsSetRequest is part of an experimental API and may change or be @@ -21298,6 +23542,113 @@ type UserSettingsSetRequest struct { type UserSettingsSetResult struct { } +// Selects the configuration directory whose user settings document should be inspected for +// unrecognized top-level keys. +// Experimental: UserSettingsUnknownKeysWarningRequest is part of an experimental API and +// may change or be removed. +// Internal: UserSettingsUnknownKeysWarningRequest is an internal SDK API and is not part of +// the public surface. +type UserSettingsUnknownKeysWarningRequest struct { + // The configuration directory, home directory and environment to resolve the settings + // document from. A host that scopes a session to its own Copilot home must pass that + // directory here, or the server's own home is inspected instead. + Context UserSettingsHostContext `json:"context"` +} + +// The unrecognized top-level keys found in the user settings document, already phrased for +// display. +// Experimental: UserSettingsUnknownKeysWarningResult is part of an experimental API and may +// change or be removed. +// Internal: UserSettingsUnknownKeysWarningResult is an internal SDK API and is not part of +// the public surface. +type UserSettingsUnknownKeysWarningResult struct { + // The warning to show the user, naming the unrecognized keys and the file they were found + // in. Absent when every top-level key is recognized, and also when the document is missing + // or could not be read, because an unknown-key list taken from a document the runtime could + // not load would name keys that may not be there. + Warning *string `json:"warning,omitempty"` +} + +// Names the user setting to merge into, and the partial object to merge. +// Experimental: UserSettingsUpdateRequest is part of an experimental API and may change or +// be removed. +// Internal: UserSettingsUpdateRequest is an internal SDK API and is not part of the public +// surface. +type UserSettingsUpdateRequest struct { + // Copilot configuration directory to write the settings document in, taking precedence over + // the server's own `COPILOT_HOME` and default home. Omit it, or pass an empty string, to + // use the directory the server resolved for itself. + ConfigDir *string `json:"configDir,omitempty"` + // Top-level user-setting key to merge into, such as `extensions`. + Key string `json:"key"` + // Single-component name for the settings document at `/settings/.json`; + // path separators, `.` and `..` are rejected. Omit it, or pass an empty string, to use the + // main settings document. + SubDir *string `json:"subDir,omitempty"` + // Value to store for `key`. When both the stored value and this value are objects, their + // fields are shallow-merged and fields this object does not name are preserved. Otherwise + // this value replaces the stored value. + Value any `json:"value"` +} + +// The stored value of the user setting after the merge. +// Experimental: UserSettingsUpdateResult is part of an experimental API and may change or +// be removed. +// Internal: UserSettingsUpdateResult is an internal SDK API and is not part of the public +// surface. +type UserSettingsUpdateResult struct { + // The key's stored value after the write: the merged object when both the stored value and + // the supplied value are objects, otherwise the supplied value. + Value any `json:"value"` +} + +// A single top-level setting subtree to validate. +// Experimental: UserSettingsValidateRequest is part of an experimental API and may change +// or be removed. +type UserSettingsValidateRequest struct { + // True to validate a removal of this key. The current settings file is not read. + Delete *bool `json:"delete,omitempty"` + // Top-level setting name to validate. + TopKey string `json:"topKey"` + // Value to validate. Omitted when `delete` is true. + Value any `json:"value,omitempty"` +} + +// Outcome of validating one top-level setting subtree. +// Experimental: UserSettingsValidateResult is part of an experimental API and may change or +// be removed. +type UserSettingsValidateResult struct { + // Problems found, empty when `ok` is true. + Issues []UserSettingsValidationIssue `json:"issues,omitzero"` + // True when the subtree is valid. + Ok bool `json:"ok"` +} + +// One problem found while validating a setting value. +// Experimental: UserSettingsValidationIssue is part of an experimental API and may change +// or be removed. +type UserSettingsValidationIssue struct { + // Human-readable description of the problem. + Message string `json:"message"` + // Where in the settings tree the problem sits, as object keys and array indices. The first + // element is the top-level key; a whole-value problem is reported as that single key. + Path []any `json:"path"` +} + +// The whole content to store in the user settings file. +// Experimental: UserSettingsWriteAllRequest is part of an experimental API and may change +// or be removed. +type UserSettingsWriteAllRequest struct { + // The settings object to store. It replaces the file's entire content, so a key absent here + // is removed. + Settings any `json:"settings"` +} + +// Experimental: UserSettingsWriteAllResult is part of an experimental API and may change or +// be removed. +type UserSettingsWriteAllResult struct { +} + // The approval to add as a session-scoped rule // Experimental: UserToolSessionApproval is part of an experimental API and may change or be // removed. @@ -24184,6 +26535,32 @@ const ( ExternalToolTextResultForLlmContentTypeText ExternalToolTextResultForLlmContentType = "text" ) +// The population a feature flag is declared for, before overrides. +// Experimental: FeatureFlagAvailability is part of an experimental API and may change or be +// removed. +type FeatureFlagAvailability string + +const ( + // Enabled only for accounts that opted into experimental features. + FeatureFlagAvailabilityExperimental FeatureFlagAvailability = "experimental" + // Disabled for everyone, before any override. + FeatureFlagAvailabilityOff FeatureFlagAvailability = "off" + // Enabled for everyone, before any override. + FeatureFlagAvailabilityOn FeatureFlagAvailability = "on" + // Enabled for any staff account, GitHub or Microsoft. + FeatureFlagAvailabilityStaff FeatureFlagAvailability = "staff" + // Enabled only for staff accounts that have also opted into experimental features. + FeatureFlagAvailabilityStaffAndExperimental FeatureFlagAvailability = "staff-and-experimental" + // Enabled for GitHub staff accounts only. + FeatureFlagAvailabilityStaffGitHub FeatureFlagAvailability = "staff-github" + // Enabled for Microsoft staff accounts only. + FeatureFlagAvailabilityStaffMicrosoft FeatureFlagAvailability = "staff-microsoft" + // Enabled for staff accounts and for accounts that opted into experimental features. + FeatureFlagAvailabilityStaffOrExperimental FeatureFlagAvailability = "staff-or-experimental" + // Enabled only for accounts on the owning team. + FeatureFlagAvailabilityTeam FeatureFlagAvailability = "team" +) + // Why the runtime is requesting a GitHub credential. // Experimental: GitHubTokenAcquireReason is part of an experimental API and may change or // be removed. @@ -24551,6 +26928,87 @@ const ( LoginProviderKindProxima LoginProviderKind = "proxima" ) +// Operation to evaluate. Currently only url is supported. +type ManagedPermissionOperationKind string + +const ( + ManagedPermissionOperationKindURL ManagedPermissionOperationKind = "url" +) + +// Managed-policy verdict only: deny blocks the URL, ask requires approval, allow approves +// under managed policy, and unmanaged leaves the normal consumer permission flow in effect. +// No verdict bypasses other security controls. +// Experimental: ManagedPermissionVerdict is part of an experimental API and may change or +// be removed. +type ManagedPermissionVerdict string + +const ( + // Managed policy allows the operation, subject to other security controls. + ManagedPermissionVerdictAllow ManagedPermissionVerdict = "allow" + // Managed policy requires the consumer to obtain approval. + ManagedPermissionVerdictAsk ManagedPermissionVerdict = "ask" + // Managed policy prohibits the operation; do not offer approval. + ManagedPermissionVerdictDeny ManagedPermissionVerdict = "deny" + // Managed policy does not decide the operation; use the consumer's normal permission flow. + ManagedPermissionVerdictUnmanaged ManagedPermissionVerdict = "unmanaged" +) + +// What retrying did for one plugin required by managed settings. +// Experimental: ManagedPluginRetryStatus is part of an experimental API and may change or +// be removed. +type ManagedPluginRetryStatus string + +const ( + // The plugin was already installed and current, so nothing was done. + ManagedPluginRetryStatusAlreadyPresent ManagedPluginRetryStatus = "already_present" + // The plugin was not prepared because another session was changing plugins or the managed + // settings changed during the retry. It is prepared again before a later message. + ManagedPluginRetryStatusDeferred ManagedPluginRetryStatus = "deferred" + // Checking, installing, or updating the plugin failed. See `error`. + ManagedPluginRetryStatusFailed ManagedPluginRetryStatus = "failed" + // The plugin was missing and is now installed. + ManagedPluginRetryStatusInstalled ManagedPluginRetryStatus = "installed" + // The session's managed settings do not require this plugin, so nothing was done. + ManagedPluginRetryStatusNotRequired ManagedPluginRetryStatus = "not_required" + // An update was available and is now installed. + ManagedPluginRetryStatusUpdated ManagedPluginRetryStatus = "updated" +) + +// Which layer disabled bypass-permissions mode. The layer matters because only a +// server-delivered disable is pinned to an account and can later be lifted by signing in +// elsewhere. +// Experimental: ManagedSettingsBypassDisableSource is part of an experimental API and may +// change or be removed. +type ManagedSettingsBypassDisableSource string + +const ( + // The account's server-delivered org policy disabled it. Pinned to the account named by + // `currentManagedIdentity`. + ManagedSettingsBypassDisableSourceManaged ManagedSettingsBypassDisableSource = "managed" + // The device/MDM layer disabled it. Outranks the account, so no server answer lifts it. + ManagedSettingsBypassDisableSourceMdm ManagedSettingsBypassDisableSource = "mdm" + // The user turned bypass mode off themselves. + ManagedSettingsBypassDisableSourceUser ManagedSettingsBypassDisableSource = "user" +) + +// What the managed-policy resolution said about bypass-permissions mode. `unknown` is a +// third answer rather than a missing one, and the caller must send it explicitly when it +// has no verdict. +// Experimental: ManagedSettingsBypassPolicyResolution is part of an experimental API and +// may change or be removed. +type ManagedSettingsBypassPolicyResolution string + +const ( + // The policy resolved and does not disable bypass mode. + ManagedSettingsBypassPolicyResolutionAllowed ManagedSettingsBypassPolicyResolution = "allowed" + // The policy resolved and disables bypass mode. + ManagedSettingsBypassPolicyResolutionDisabled ManagedSettingsBypassPolicyResolution = "disabled" + // No verdict: the fetch failed, or the service declared a policy this build may not + // enforce. Whatever is in force is preserved, because reverting to permissive on an + // unreadable policy would widen access. + ManagedSettingsBypassPolicyResolutionUnknown ManagedSettingsBypassPolicyResolution = "unknown" +) + // A channel accepted by managedSettings.compose. // Experimental: ManagedSettingsChannel is part of an experimental API and may change or be // removed. @@ -24577,6 +27035,46 @@ const ( ManagedSettingsDiagnosticSeverityWarning ManagedSettingsDiagnosticSeverity = "warning" ) +// Severity of a diagnostic emitted while ingesting managed settings. +// Experimental: ManagedSettingsLogLevel is part of an experimental API and may change or be +// removed. +type ManagedSettingsLogLevel string + +const ( + // Detail useful only when diagnosing an ingestion. + ManagedSettingsLogLevelDebug ManagedSettingsLogLevel = "debug" + // A failure that changed the outcome of the ingestion. + ManagedSettingsLogLevelError ManagedSettingsLogLevel = "error" + // Ordinary progress worth surfacing under a verbose flag. + ManagedSettingsLogLevelInfo ManagedSettingsLogLevel = "info" + // A condition the operator should see but which did not stop the ingestion. + ManagedSettingsLogLevelWarning ManagedSettingsLogLevel = "warning" +) + +// Which channel registered the policy helper that produced a helper layer. +// Experimental: ManagedSettingsPolicyHelperAuthority is part of an experimental API and may +// change or be removed. +type ManagedSettingsPolicyHelperAuthority string + +const ( + // The helper was registered by the device (MDM or managed file) layer. + ManagedSettingsPolicyHelperAuthorityDevice ManagedSettingsPolicyHelperAuthority = "device" + // The helper was registered by the account's server-delivered policy. + ManagedSettingsPolicyHelperAuthorityServer ManagedSettingsPolicyHelperAuthority = "server" +) + +// The operation that requires a policy-helper response. +// Experimental: ManagedSettingsPolicyHelperReason is part of an experimental API and may +// change or be removed. +type ManagedSettingsPolicyHelperReason string + +const ( + // Resolve policy before plugin activation. + ManagedSettingsPolicyHelperReasonPluginActivation ManagedSettingsPolicyHelperReason = "pluginActivation" + // Resolve policy for session startup. + ManagedSettingsPolicyHelperReasonSessionStart ManagedSettingsPolicyHelperReason = "sessionStart" +) + // Summary of which managed-settings channels contributed to the effective session policy. // Use the per-channel booleans for exact provenance. // Experimental: ManagedSettingsResolvedSource is part of an experimental API and may change @@ -24600,6 +27098,44 @@ const ( ManagedSettingsResolvedSourceServer ManagedSettingsResolvedSource = "server" ) +// How an ingestion may use the on-disk server-policy cache. +// Experimental: ManagedSettingsServerPolicyCacheMode is part of an experimental API and may +// change or be removed. +type ManagedSettingsServerPolicyCacheMode string + +const ( + // Read cached policy without a network request. + ManagedSettingsServerPolicyCacheModeCacheOnly ManagedSettingsServerPolicyCacheMode = "cacheOnly" + // Ignore the cache entirely and rely on the live fetch alone. + ManagedSettingsServerPolicyCacheModeDisabled ManagedSettingsServerPolicyCacheMode = "disabled" + // Serve a cached entry only as a fallback after a failed live fetch. + ManagedSettingsServerPolicyCacheModeFallbackOnly ManagedSettingsServerPolicyCacheMode = "fallbackOnly" + // Serve a recent cache entry before a live fetch. + ManagedSettingsServerPolicyCacheModePreferFresh ManagedSettingsServerPolicyCacheMode = "preferFresh" +) + +// How the server layer of an ingestion was resolved. +// Experimental: ManagedSettingsServerPolicyResolution is part of an experimental API and +// may change or be removed. +type ManagedSettingsServerPolicyResolution string + +const ( + // No cached policy or confirmed no-policy result exists. + ManagedSettingsServerPolicyResolutionCacheMiss ManagedSettingsServerPolicyResolution = "cacheMiss" + // No policy could be resolved and the host applies the fail-closed gates. + ManagedSettingsServerPolicyResolutionFailClosed ManagedSettingsServerPolicyResolution = "failClosed" + // No policy could be resolved and the host proceeds unmanaged. + ManagedSettingsServerPolicyResolutionFailOpen ManagedSettingsServerPolicyResolution = "failOpen" + // A live fetch failed and a cached entry was served in its place. + ManagedSettingsServerPolicyResolutionFallbackCache ManagedSettingsServerPolicyResolution = "fallbackCache" + // A recent cached entry was served without a live fetch. + ManagedSettingsServerPolicyResolutionFreshCache ManagedSettingsServerPolicyResolution = "freshCache" + // A live fetch succeeded. + ManagedSettingsServerPolicyResolutionLive ManagedSettingsServerPolicyResolution = "live" + // No fetch was attempted because no usable token was available. + ManagedSettingsServerPolicyResolutionSkippedNoToken ManagedSettingsServerPolicyResolution = "skippedNoToken" +) + // Allowed values for the `McpAppsHostContextDetailsAvailableDisplayMode` enumeration. // Experimental: MCPAppsHostContextDetailsAvailableDisplayMode is part of an experimental // API and may change or be removed. @@ -26203,6 +28739,35 @@ const ( ProviderEndpointWireAPIResponses ProviderEndpointWireAPI = "responses" ) +// Identity scope of a monthly usage reading. +// Experimental: ProviderMonthlyUsageScope is part of an experimental API and may change or +// be removed. +type ProviderMonthlyUsageScope string + +const ( + // The service did not establish a recognized usage scope. + ProviderMonthlyUsageScopeUnknown ProviderMonthlyUsageScope = "unknown" + // Consumption of the authenticated user within the parent observation's service and + // returned cycle. + ProviderMonthlyUsageScopeUser ProviderMonthlyUsageScope = "user" +) + +// Availability of a service's monthly consumption reading. +// Experimental: ProviderMonthlyUsageState is part of an experimental API and may change or +// be removed. +type ProviderMonthlyUsageState string + +const ( + // The service reported a usable consumption reading, including an explicit zero. + ProviderMonthlyUsageStateAvailable ProviderMonthlyUsageState = "available" + // The service found no usage policy; no consumption amount is known. + ProviderMonthlyUsageStateNoPolicy ProviderMonthlyUsageState = "no_policy" + // The read failed or its data could not be used; consumption is unknown. + ProviderMonthlyUsageStateUnavailable ProviderMonthlyUsageState = "unavailable" + // The service reported an unrecognized usage state. + ProviderMonthlyUsageStateUnknown ProviderMonthlyUsageState = "unknown" +) + // Access policy reported by the quota service. // Experimental: ProviderQuotaAccessState is part of an experimental API and may change or // be removed. @@ -26381,6 +28946,8 @@ const ( // Review or widen the sandbox policy. The blocked path or host is named by the accompanying // message or by the tool result the action arrived with. RemediationActionReviewSandboxPolicy RemediationAction = "review_sandbox_policy" + // Discard conversation history back through the blocked request before continuing. + RemediationActionRewindConversation RemediationAction = "rewind_conversation" // Inspect which account is currently authenticated before deciding what to change. RemediationActionShowAccount RemediationAction = "show_account" // Authenticate again with the Copilot backend. The current credential is absent, expired, @@ -26448,6 +29015,18 @@ const ( RemoteSessionModeOn RemoteSessionMode = "on" ) +// Which repository settings file to edit. +// Experimental: RepoSettingsEditScope is part of an experimental API and may change or be +// removed. +type RepoSettingsEditScope string + +const ( + // The local repository settings file. + RepoSettingsEditScopeLocal RepoSettingsEditScope = "local" + // The shared repository settings file. + RepoSettingsEditScopeRepo RepoSettingsEditScope = "repo" +) + // Output format discriminator. Currently only json_schema is supported. type ResponseFormatType string @@ -27018,6 +29597,36 @@ const ( SettableAuthInfoTypeUser SettableAuthInfoType = "user" ) +// One atomic settings mutation. +// Experimental: SettingsEditOperation is part of an experimental API and may change or be +// removed. +type SettingsEditOperation string + +const ( + // Add one string to a list, unless present. + SettingsEditOperationAddItem SettingsEditOperation = "add-item" + // Remove the setting without supplying a value. + SettingsEditOperationRemove SettingsEditOperation = "remove" + // Remove one string from a list, unless absent. + SettingsEditOperationRemoveItem SettingsEditOperation = "remove-item" + // Set one scalar value. + SettingsEditOperationSet SettingsEditOperation = "set" +) + +// Whether one atomic settings edit changed a file. +// Experimental: SettingsEditStatus is part of an experimental API and may change or be +// removed. +type SettingsEditStatus string + +const ( + // The edit changed the setting and was stored. + SettingsEditStatusChanged SettingsEditStatus = "changed" + // The existing settings file could not be edited safely; nothing was written. + SettingsEditStatusInvalid SettingsEditStatus = "invalid" + // The setting already had the requested state. + SettingsEditStatusUnchanged SettingsEditStatus = "unchanged" +) + // Controls automatic non-interactive profile loading where supported. Explicit initScripts // are unaffected. // Experimental: ShellInitProfile is part of an experimental API and may change or be @@ -27327,6 +29936,45 @@ const ( SlashCommandModelPickerDialogKindModelPicker SlashCommandModelPickerDialogKind = "model-picker" ) +// What the one-time staff log-level migration did. +// Experimental: StaffLogLevelMigrationStatus is part of an experimental API and may change +// or be removed. +type StaffLogLevelMigrationStatus string + +const ( + // The migration already ran; the stored completion timestamp is returned unchanged. + StaffLogLevelMigrationStatusCurrent StaffLogLevelMigrationStatus = "current" + // The account is not a staff account, so the migration does not apply and nothing was + // recorded. + StaffLogLevelMigrationStatusIneligible StaffLogLevelMigrationStatus = "ineligible" + // The migration ran now. The persisted settings may have changed, so the host should adopt + // the returned settings when they are present. + StaffLogLevelMigrationStatusRefreshed StaffLogLevelMigrationStatus = "refreshed" +) + +// What the one-time staff update-channel migration did. +// Experimental: StaffUpdateChannelMigrationStatus is part of an experimental API and may +// change or be removed. +type StaffUpdateChannelMigrationStatus string + +const ( + // The persisted channel was already stable, so nothing changed, but the migration is now + // recorded as done. + StaffUpdateChannelMigrationStatusCompleted StaffUpdateChannelMigrationStatus = "completed" + // The migration already ran within the cutoff window; the stored completion timestamp is + // returned unchanged. + StaffUpdateChannelMigrationStatusCurrent StaffUpdateChannelMigrationStatus = "current" + // The account is on the experimental channel, which the migration deliberately leaves + // alone, and the migration is recorded as done. + StaffUpdateChannelMigrationStatusExcludedExperimental StaffUpdateChannelMigrationStatus = "excluded-experimental" + // The account is not a staff account, so the migration does not apply and nothing was + // recorded. + StaffUpdateChannelMigrationStatusIneligible StaffUpdateChannelMigrationStatus = "ineligible" + // The persisted update channel was changed to stable. Only this outcome requires the host + // to reload its configuration. + StaffUpdateChannelMigrationStatusMigrated StaffUpdateChannelMigrationStatus = "migrated" +) + // Context tier override for matching subagents // Experimental: SubagentSettingsEntryContextTier is part of an experimental API and may // change or be removed. @@ -27653,6 +30301,23 @@ const ( UISessionLimitsExhaustedResponseActionUnset UISessionLimitsExhaustedResponseAction = "unset" ) +// What the one-time obsolete-settings cleanup did to the document. +// Experimental: UserSettingsMigrateObsoleteOnDiskStatus is part of an experimental API and +// may change or be removed. +type UserSettingsMigrateObsoleteOnDiskStatus string + +const ( + // Obsolete keys were removed and the document was rewritten. + UserSettingsMigrateObsoleteOnDiskStatusChanged UserSettingsMigrateObsoleteOnDiskStatus = "changed" + // The document could not be parsed, so it was left untouched rather than rewritten from a + // partial read. + UserSettingsMigrateObsoleteOnDiskStatusInvalid UserSettingsMigrateObsoleteOnDiskStatus = "invalid" + // No settings document exists, so there was nothing to clean. + UserSettingsMigrateObsoleteOnDiskStatusMissing UserSettingsMigrateObsoleteOnDiskStatus = "missing" + // The document parsed and held nothing obsolete, so nothing was written. + UserSettingsMigrateObsoleteOnDiskStatusUnchanged UserSettingsMigrateObsoleteOnDiskStatus = "unchanged" +) + // Kind discriminator for UserToolSessionApproval. type UserToolSessionApprovalKind string @@ -28695,6 +31360,42 @@ func (a *ServerManagedSettingsAPI) Validate(ctx context.Context, params *Managed return &result, nil } +// Experimental: ServerManagedSettingsPermissionsAPI contains experimental APIs that may +// change or be removed. +type ServerManagedSettingsPermissionsAPI serverAPI + +// Evaluate pure, sessionless evaluation of operations against a supplied +// permissionsContext. Performs no policy discovery, I/O, prompting, navigation, permission +// grants, or sandbox changes. Obtain the context from managedSettings.resolve or +// session.managedSettings.get, or reuse an in-memory context. Results preserve input order +// and duplicates. Consumers must block deny, obtain approval for ask, and use their normal +// permission flow for unmanaged. Unknown policy returns failClosed with every operation +// denied. This is not a substitute for user permissions or other security controls. +// +// RPC method: managedSettings.permissions.evaluate. +// +// Parameters: Managed policy context and ordered operations for pure, sessionless +// evaluation. +// +// Returns: Ordered managed permission verdicts and the supplied policy's fail-closed +// posture. +func (a *ServerManagedSettingsPermissionsAPI) Evaluate(ctx context.Context, params *ManagedPermissionsEvaluateRequest) (*ManagedSettingsPermissionsEvaluateResult, error) { + raw, err := a.client.Request(ctx, "managedSettings.permissions.evaluate", params) + if err != nil { + return nil, err + } + var result ManagedSettingsPermissionsEvaluateResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: Permissions returns experimental APIs that may change or be removed. +func (s *ServerManagedSettingsAPI) Permissions() *ServerManagedSettingsPermissionsAPI { + return (*ServerManagedSettingsPermissionsAPI)(s) +} + // Experimental: ServerMCPAPI contains experimental APIs that may change or be removed. type ServerMCPAPI serverAPI @@ -29410,6 +32111,48 @@ func (a *ServerSandboxAPI) GetHostSupport(ctx context.Context) (*SandboxHostSupp // removed. type ServerSandboxProxyCaAPI serverAPI +// CheckInstallationSupport reports whether this runtime process can offer installing the +// sandbox credential-proxy certificate authority into OS trust. +// +// RPC method: sandbox.proxyCa.checkInstallationSupport. +// +// Returns: True when this process can offer installing the sandbox credential-proxy +// certificate authority into OS trust. +func (a *ServerSandboxProxyCaAPI) CheckInstallationSupport(ctx context.Context) (*SandboxProxyCaCheckInstallationSupportResult, error) { + raw, err := a.client.Request(ctx, "sandbox.proxyCa.checkInstallationSupport", nil) + if err != nil { + return nil, err + } + var result SandboxProxyCaCheckInstallationSupportResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// CheckRelevance reports whether this sandbox configuration uses the credential proxy and +// can need certificate-authority setup. +// +// RPC method: sandbox.proxyCa.checkRelevance. +// +// Parameters: Identifies the credential hosts that the persistent certificate authority of +// the sandbox credential proxy must cover. The runtime always adds the hosts from the saved +// user settings. +// +// Returns: True when the sandbox configuration uses the credential proxy and can need +// certificate-authority setup. +func (a *ServerSandboxProxyCaAPI) CheckRelevance(ctx context.Context, params *SandboxProxyCaRequest) (*SandboxProxyCaCheckRelevanceResult, error) { + raw, err := a.client.Request(ctx, "sandbox.proxyCa.checkRelevance", params) + if err != nil { + return nil, err + } + var result SandboxProxyCaCheckRelevanceResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // Creates the persistent certificate authority of the sandbox credential proxy if none is // stored, without changing OS trust, and returns the path of its public certificate. Keeps // an existing certificate authority, even one that must be rotated. Fails where OS trust is @@ -30607,6 +33350,105 @@ type internalServerAPI struct { client *jsonrpc2.Client } +// Experimental: InternalServerAccountAPI contains experimental APIs that may change or be +// removed. +type InternalServerAccountAPI internalServerAPI + +// DescribeAuthInfo renders the sign-in banner and the one-line account description the +// runtime uses for an authentication identity. The runtime words these in several places +// for its own callers, so a host that announces a sign-in or an account switch should +// render them from here rather than composing its own, which would let the two spellings +// drift apart. +// +// RPC method: account.describeAuthInfo. +// +// Parameters: The authentication identity whose shared wording should be rendered. +// +// Returns: The wording the runtime uses for an authentication identity. +// Internal: DescribeAuthInfo is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerAccountAPI) DescribeAuthInfo(ctx context.Context, params *AccountDescribeAuthInfoRequest) (*AccountDescribeAuthInfoResult, error) { + raw, err := a.client.Request(ctx, "account.describeAuthInfo", params) + if err != nil { + return nil, err + } + var result AccountDescribeAuthInfoResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetGistSupport reports whether the supplied identity can create gists. Gists are +// unavailable on GitHub Enterprise Cloud with data residency, and to Enterprise Managed +// Users on github.com, so a host that offers gist actions should hide or disable them when +// this reports no support. +// +// RPC method: account.getGistSupport. +// +// Parameters: The authentication identity whose gist support should be reported. +// +// Returns: Whether the identity can create gists, and why not when it cannot. +// Internal: GetGistSupport is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerAccountAPI) GetGistSupport(ctx context.Context, params *AccountGetGistSupportRequest) (*AccountGetGistSupportResult, error) { + raw, err := a.client.Request(ctx, "account.getGistSupport", params) + if err != nil { + return nil, err + } + var result AccountGetGistSupportResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetTokenEnvVar names the environment variable supplying a classic personal access token, +// if one is set. A classic token cannot be upgraded in place, so a host that finds one +// should tell the user which variable to unset rather than starting an interactive sign-in +// that the variable would override. +// +// RPC method: account.getTokenEnvVar. +// +// Returns: The environment variable supplying a classic personal access token, when one is +// set. +// Internal: GetTokenEnvVar is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerAccountAPI) GetTokenEnvVar(ctx context.Context) (*AccountGetTokenEnvVarResult, error) { + raw, err := a.client.Request(ctx, "account.getTokenEnvVar", nil) + if err != nil { + return nil, err + } + var result AccountGetTokenEnvVarResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetWebUrl resolves a path on the identity's GitHub host into an absolute URL, so a host +// can link to settings or documentation on the enterprise instance the user is signed in to +// rather than to github.com. Falls back to the public host when no identity is supplied. +// +// RPC method: account.getWebUrl. +// +// Parameters: A path to resolve against the GitHub host an identity is signed in to. +// +// Returns: An absolute URL on the identity's GitHub host. +// Internal: GetWebUrl is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerAccountAPI) GetWebUrl(ctx context.Context, params *AccountGetWebURLRequest) (*AccountGetWebURLResult, error) { + raw, err := a.client.Request(ctx, "account.getWebUrl", params) + if err != nil { + return nil, err + } + var result AccountGetWebURLResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // Experimental: InternalServerAgentsAPI contains experimental APIs that may change or be // removed. type InternalServerAgentsAPI internalServerAPI @@ -30745,6 +33587,95 @@ func (a *InternalServerAgentsAPI) GetBuiltins(ctx context.Context) (*AgentsGetBu return &result, nil } +// Experimental: InternalServerFeatureFlagsAPI contains experimental APIs that may change or +// be removed. +type InternalServerFeatureFlagsAPI internalServerAPI + +// ListMetadata lists every feature flag this runtime declares, with the population each one +// is declared for, its experimental description, and the online-experiment flag it maps to. +// This is the static catalog: it describes how each flag is defined, not whether it is on +// for anyone. Use `featureFlags.resolve` for static values with caller overrides, not +// active runtime values or experiment assignments. +// +// RPC method: featureFlags.listMetadata. +// +// Returns: Every feature flag this runtime declares, with the shape each one was declared +// with. +// Internal: ListMetadata is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerFeatureFlagsAPI) ListMetadata(ctx context.Context) (*FeatureFlagsMetadataResult, error) { + raw, err := a.client.Request(ctx, "featureFlags.listMetadata", nil) + if err != nil { + return nil, err + } + var result FeatureFlagsMetadataResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Resolve computes static feature flags from the supplied population and overrides without +// changing runtime state or verifying audience membership. Starts from declared +// availability, then applies direct feature-flag environment values, user settings, and +// explicit `flagOverrides`. It does not apply `COPILOT_EXP_*` experiment assignments, +// authenticated startup gates, or active runtime state. Settings include +// `enabledFeatureFlags`, the legacy `feature_flags.enabled` list, and supported convenience +// settings. Supply `baseFlags` to use an existing flag set instead of declared +// availability. Omit `isStaffGitHub` or `isStaffMicrosoft` when membership was never +// recorded: a legacy staff account then counts as both populations. +// +// RPC method: featureFlags.resolve. +// +// Parameters: Caller population and overrides for static feature-flag resolution, +// independent of experiment assignments and active runtime state. +// +// Returns: The resolved feature flags, keyed by flag name. +// Internal: Resolve is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerFeatureFlagsAPI) Resolve(ctx context.Context, params *FeatureFlagsResolveRequest) (*FeatureFlagsResolveResult, error) { + raw, err := a.client.Request(ctx, "featureFlags.resolve", params) + if err != nil { + return nil, err + } + var result FeatureFlagsResolveResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// ResolveStartupGates resolves named gates against this installation's cached +// online-experiment assignment, for the account the runtime is already authenticated as. +// Unlike `featureFlags.resolve`, which layers static availability with the populations +// supplied, this reads the experiment assignment the runtime cached for its own current +// authentication, so a launch can honor a rollout before any session exists. The account is +// never taken from the request: naming one would read another account's cached assignments. +// The cached assignment answers immediately and any network refresh happens behind it, so +// this call does not wait on the network. Not available to an extension, which cannot carry +// the user's consent to read their assignment. +// +// RPC method: featureFlags.resolveStartupGates. +// +// Parameters: Caller population and startup inputs to resolve named online-experiment gates +// for. Carries no account: the runtime resolves the assignment for the authentication it +// already holds. +// +// Returns: The resolved gates, keyed by flag name. +// Internal: ResolveStartupGates is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerFeatureFlagsAPI) ResolveStartupGates(ctx context.Context, params *FeatureFlagsResolveStartupGatesRequest) (*FeatureFlagsResolveStartupGatesResult, error) { + raw, err := a.client.Request(ctx, "featureFlags.resolveStartupGates", params) + if err != nil { + return nil, err + } + var result FeatureFlagsResolveStartupGatesResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // Experimental: InternalServerGitAPI contains experimental APIs that may change or be // removed. type InternalServerGitAPI internalServerAPI @@ -31016,12 +33947,12 @@ func (a *InternalServerGlobalStateAPI) LoadForConfigDir(ctx context.Context, par // asked a one-off question, or completed a migration, so the next run can skip it. Only the // named key is replaced and the rest of the document is preserved, which lets two writers // record different flags without overwriting each other; passing no value removes the key -// instead. Only the keys a host records itself are writable: `appInstallNudgeResponded`, -// `appTipShown`, `askedSetupTerminals`, `autoFeedbackLastPromptedAt`, `firstLaunchAt`, -// `recentModelIds`, `sandboxCredentialProxyCaDeclined` and `sandboxOnboardingShown`. Every -// other key is refused, including `installedPlugins`, the stored credentials, -// `trustedFolders`, the staff flags and the signed-in accounts. Plugin enablement must use -// the plugin APIs, which apply repository and managed-policy checks. +// instead. Only the keys a host records itself are writable: `appTipShown`, +// `askedSetupTerminals`, `autoFeedbackLastPromptedAt`, `firstLaunchAt`, `recentModelIds`, +// `sandboxCredentialProxyCaDeclined` and `sandboxOnboardingShown`. Every other key is +// refused, including `installedPlugins`, the stored credentials, `trustedFolders`, the +// staff flags and the signed-in accounts. Plugin enablement must use the plugin APIs, which +// apply repository and managed-policy checks. // // RPC method: globalState.writeKey. // @@ -31155,6 +34086,1235 @@ func (a *InternalServerHostAPI) ReleaseSession(ctx context.Context, params *Host return &result, nil } +// Experimental: InternalServerInstructionsAPI contains experimental APIs that may change or +// be removed. +type InternalServerInstructionsAPI internalServerAPI + +// ClearCache drops the runtime's in-memory cache of discovered custom instructions so the +// next discovery reads them from disk again. A host calls this after it changes instruction +// files, or after it trusts a folder whose instructions an earlier discovery skipped, so +// that a session started afterwards sees the current files instead of a cached earlier +// answer. The call performs no I/O of its own and removes nothing from disk; it only +// invalidates, so the cost lands on the next discovery. Returns once the cache is +// clear, which is what lets a caller order a session start after it. Marked internal +// because it exists to carry a CLI call site off the napi boundary onto the SDK contract; +// it is migration plumbing, not a surface consumers are meant to depend on. +// +// RPC method: instructions.clearCache. +// Internal: ClearCache is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerInstructionsAPI) ClearCache(ctx context.Context) (*InstructionsClearCacheResult, error) { + raw, err := a.client.Request(ctx, "instructions.clearCache", nil) + if err != nil { + return nil, err + } + var result InstructionsClearCacheResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// DiscoverRepoSources discovers the instruction sources for one caller-named location and +// returns them in the order the runtime applies them. `discover` answers for a set of +// project paths and derives the rest of its scope itself; this one instead takes every +// argument from the caller — the extra directories to scan and whether to descend into +// nested instruction files — and honors them as given. It also includes instruction +// directories from trusted repository settings, matching native repository discovery. Use +// it when the caller knows its own scope and no session exists to derive that scope from, +// such as a host reporting how many instruction files a directory contributes before any +// session starts. Marked internal because it exists to carry a CLI call site off the napi +// boundary onto the SDK contract; it is migration plumbing, not a surface consumers are +// meant to depend on. +// +// RPC method: instructions.discoverRepoSources. +// +// Parameters: Explicit discovery arguments for one location, supplied by the caller rather +// than derived from a session. +// +// Returns: Instruction sources discovered across user, repository, and plugin sources. +// Internal: DiscoverRepoSources is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerInstructionsAPI) DiscoverRepoSources(ctx context.Context, params *InstructionsDiscoverRepoSourcesRequest) (*ServerInstructionSourceList, error) { + raw, err := a.client.Request(ctx, "instructions.discoverRepoSources", params) + if err != nil { + return nil, err + } + var result ServerInstructionSourceList + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetInitPrompt returns the prompt the `init` command runs to author repository +// instructions. A host offering its own initialize action sends this text so the +// instructions it produces match the ones the CLI produces. The text is fixed for a build +// and depends on no configuration, session or repository. +// +// RPC method: instructions.getInitPrompt. +// +// Returns: The prompt the init command runs. +// Internal: GetInitPrompt is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerInstructionsAPI) GetInitPrompt(ctx context.Context) (*InstructionsInitPromptResult, error) { + raw, err := a.client.Request(ctx, "instructions.getInitPrompt", nil) + if err != nil { + return nil, err + } + var result InstructionsInitPromptResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: InternalServerManagedSettingsAPI contains experimental APIs that may change +// or be removed. +type InternalServerManagedSettingsAPI internalServerAPI + +// ApplyControls stamps an effective managed policy's individually-controlled entries onto a +// user settings document, returning the settings a host should actually act on. The managed +// layer wins on every plugin spec and marketplace name it names, and leaves everything it +// does not name as the user wrote it. Hosts use this rather than merging the two documents +// themselves, because the merge is per entry rather than per document and a whole-document +// overlay would either discard the user's unmanaged entries or let a user entry outrank a +// managed one. Omitting the managed layer returns the settings unchanged, which is what an +// unmanaged host wants. Rejects when either document is present but is not a JSON object +// rather than treating it as absent, since silently dropping a malformed policy hands the +// user's layer the win on exactly the entries the policy exists to control. +// +// RPC method: managedSettings.applyControls. +// +// Parameters: A user settings document and the effective managed policy whose controlled +// entries should be stamped onto it. +// +// Returns: The user settings with the managed layer's controlled entries stamped on. +// Internal: ApplyControls is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerManagedSettingsAPI) ApplyControls(ctx context.Context, params *ManagedSettingsApplyControlsParams) (*ManagedSettingsApplyControlsResult, error) { + raw, err := a.client.Request(ctx, "managedSettings.applyControls", params) + if err != nil { + return nil, err + } + var result ManagedSettingsApplyControlsResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Ingest resolves managed (enterprise policy) settings for a host from every layer at once: +// the account's server-delivered policy, the device/MDM layer, and a platform policy +// helper. Hosts use this instead of fetching and composing the layers themselves, because +// the per-layer precedence and the fail-closed rules that apply when a layer cannot be +// confirmed are the runtime's own and must match the policy a session will later enforce. +// It does not retain or apply anything to process-global policy state, so a host may call +// it before any session exists — which is the point, since plugin activation and one-shot +// commands resolve policy before a session is available to carry it. The returned per-layer +// outcome flags are load-bearing rather than diagnostic: a host must distinguish an +// unmanaged device from a managed device whose policy could not be confirmed, and apply its +// fail-closed gates in the second case. Reports its diagnostics in `logMessages` rather +// than logging them, so a host that runs before a log sink exists still sees them. +// +// RPC method: managedSettings.ingest. +// +// Parameters: Inputs describing the host and account an ingestion should resolve managed +// policy for. +// +// Returns: The managed policy layers an ingestion resolved, together with the per-layer +// outcome flags a host needs to decide how much to trust the result. The flags are reported +// rather than folded into the settings because a host must be able to distinguish an +// unmanaged device from a managed device whose policy could not be confirmed, and apply its +// fail-closed gates in the second case. +// Internal: Ingest is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerManagedSettingsAPI) Ingest(ctx context.Context, params *ManagedSettingsIngestParams) (*ManagedSettingsIngestResult, error) { + raw, err := a.client.Request(ctx, "managedSettings.ingest", params) + if err != nil { + return nil, err + } + var result ManagedSettingsIngestResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// LoadRemoteControl resolves the managed `remoteControl` policy for an account on its own, +// for a host deciding whether a remote-control session may start. Composes the same layers +// as `managedSettings.ingest` and returns only that one key, so a host does not have to +// know which layer won. An absent policy means the account is unmanaged for this capability +// and the host may proceed; that default is safe only while absence really does mean "no +// policy", so a service refusal that says an account policy exists which this client is too +// old to enforce resolves to an explicit disabled policy instead of to absence. A +// resolution that cannot be completed is rejected rather than answered as absence, for the +// same reason. +// +// RPC method: managedSettings.loadRemoteControl. +// +// Parameters: Inputs describing the host and account whose managed remote-control policy +// should be resolved. Mirrors `managedSettings.ingest`, and deliberately carries no +// retention decision: reading this gate never becomes this runtime process's startup policy. +// +// Returns: The managed remote-control policy in force for an account, if any. An absent +// policy means the account is unmanaged for this capability, which a host reads as allowed +// — so absence here is a positive statement that no policy applies, never a stand-in for a +// resolution that failed. A resolution that cannot be completed is rejected instead. +// Internal: LoadRemoteControl is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerManagedSettingsAPI) LoadRemoteControl(ctx context.Context, params *ManagedSettingsLoadRemoteControlParams) (*ManagedSettingsLoadRemoteControlResult, error) { + raw, err := a.client.Request(ctx, "managedSettings.loadRemoteControl", params) + if err != nil { + return nil, err + } + var result ManagedSettingsLoadRemoteControlResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// ParseClientValue validates the managed settings a client is supplying for a session it is +// about to open, and returns them reduced to the keys a client is allowed to set. A +// client-supplied policy is not the same thing as an administrator's: it may only narrow +// permissions, so this accepts `permissions` and rejects every other key outright rather +// than ignoring it. Rejecting is the point — a host that silently dropped an unsupported +// key would report a policy as applied while running without it. Hosts call this before +// opening the session rather than letting the open fail, so a malformed policy is reported +// against the request that carried it. The runtime applies the same rule again when the +// session starts, so this cannot be used to smuggle a wider document past it. An accepted +// document that sets nothing comes back as an empty object, which is distinct from +// supplying nothing at all. +// +// RPC method: managedSettings.parseClientValue. +// +// Parameters: The client-supplied managed settings to validate and normalize. Omitting them +// is valid and yields no policy. +// +// Returns: The accepted managed settings, reduced to the keys a client is allowed to set. +// Internal: ParseClientValue is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerManagedSettingsAPI) ParseClientValue(ctx context.Context, params *ManagedSettingsParseClientValueParams) (*ManagedSettingsParseClientValueResult, error) { + raw, err := a.client.Request(ctx, "managedSettings.parseClientValue", params) + if err != nil { + return nil, err + } + var result ManagedSettingsParseClientValueResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// ReadDevice discovers device-managed settings the way the runtime's own device layer does, +// and reports the sandbox floor and the load outcome alongside the validated document. This +// differs from `managedSettings.read` in three ways a policy-enforcing host depends on: a +// document that validates to an empty object is reported as absent rather than as an empty +// policy, a discovery error does not suppress a document that still validates, and a +// failure is reported as a flag rather than only as error text. Hosts that compose the +// device layer into an effective policy use this, because `managedSettings.read` cannot +// distinguish "no device policy" from "device policy could not be read" and would silently +// hand the user's own layer the win. +// +// RPC method: managedSettings.readDevice. +// +// Returns: The device-managed settings layer, with the sandbox floor and the load outcome a +// host needs to compose it into an effective policy. +// Internal: ReadDevice is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerManagedSettingsAPI) ReadDevice(ctx context.Context) (*ManagedSettingsReadDeviceResult, error) { + raw, err := a.client.Request(ctx, "managedSettings.readDevice", nil) + if err != nil { + return nil, err + } + var result ManagedSettingsReadDeviceResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// ResolveBypassDisableTransition decides what a host must do to its bypass-permissions +// state after a managed-policy resolution lands, given the state already in force and the +// account now signed in. A host holds two pieces of state — which layer disabled bypass +// mode, and for a server-delivered disable, the account it was pinned to — and cannot +// decide the next step from the new policy alone. Three rules make that so, and each one is +// a control rather than a preference. A device/MDM disable outranks the account, so a later +// server answer never lifts it. An unknown policy, which is what a failed fetch or a +// refusal this build cannot enforce yields, preserves whatever is in force instead of +// reverting to permissive. And a server disable is lifted only when the account it was +// pinned to is no longer the signed-in one, which is what makes signing in to an +// unrestricted account recover without a restart while a reconnect on the same restricted +// account does not. Hosts call this rather than reimplementing it because getting the +// pinning backwards either strands a disable on an account that never carried one or drops +// one that still applies. `setDisabledState` is omitted when the host must leave its stored +// flag untouched, which is distinct from being told to set it to false. +// +// RPC method: managedSettings.resolveBypassDisableTransition. +// +// Parameters: The bypass-permissions state a host holds today, the account now signed in, +// and what the managed-policy resolution said. +// +// Returns: The bypass-permissions state a host should hold after the resolution, and what +// it must do to get there. +// Internal: ResolveBypassDisableTransition is part of the SDK's internal +// handshake/plumbing; external callers should not use it. +func (a *InternalServerManagedSettingsAPI) ResolveBypassDisableTransition(ctx context.Context, params *ManagedSettingsResolveBypassDisableTransitionParams) (*ManagedSettingsResolveBypassDisableTransitionResult, error) { + raw, err := a.client.Request(ctx, "managedSettings.resolveBypassDisableTransition", params) + if err != nil { + return nil, err + } + var result ManagedSettingsResolveBypassDisableTransitionResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// ResolveDeviceForceLoginOrgsGate evaluates the device-channel enterprise sign-in pin (MDM +// plist, registry, or managed-settings file) against a credential, without resolving any +// server-delivered policy. This is the gate an entry point runs before it branches, and the +// only one that can refuse a developer's first sign-in: it is local, needs no credential, +// and a server-delivered pin only ever reaches accounts already inside the organization. It +// fails closed by construction — a discovery that cannot determine whether a pin exists +// refuses rather than allows, because a control that fails open under a triggerable +// condition is not a control. Hosts that also want the server channel should call +// `managedSettings.resolveStartupPolicy`, which folds both channels into one verdict. +// +// RPC method: managedSettings.resolveDeviceForceLoginOrgsGate. +// +// Parameters: The credential and environment to evaluate the device-channel sign-in pin +// against. +// +// Returns: The verdict of the `forceLoginOrgs` enterprise sign-in pin for one credential. A +// managed device may pin Copilot to an approved set of GitHub organizations so a developer +// cannot sign in with a personal — or another enterprise's — account and escape every +// control that keys off the signed-in identity. A host must refuse to proceed when +// `allowed` is false; the remaining fields explain the refusal rather than qualifying it. +// Internal: ResolveDeviceForceLoginOrgsGate is part of the SDK's internal +// handshake/plumbing; external callers should not use it. +func (a *InternalServerManagedSettingsAPI) ResolveDeviceForceLoginOrgsGate(ctx context.Context, params *ManagedSettingsResolveDeviceForceLoginOrgsGateParams) (*ForceLoginOrgsDecision, error) { + raw, err := a.client.Request(ctx, "managedSettings.resolveDeviceForceLoginOrgsGate", params) + if err != nil { + return nil, err + } + var result ForceLoginOrgsDecision + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// ResolveEffectiveValue composes the supplied server, device/MDM, device sandbox floor and +// policy-helper layers into the single effective managed policy a host should enforce, +// resolving each key across the layers by the runtime's own precedence rules. Hosts that +// assemble managed layers themselves use this so their precedence matches the runtime's +// exactly instead of reimplementing it. The composition is not a pure function of its +// inputs: an advisory sandbox force-on that arrived over the server channel is declined +// when this host cannot run a sandbox, so the result reflects the runtime host's +// capabilities, and the returned settings carry `sandboxAdvisoryFloorDeclined: true` when +// that happened. Enforced channels (device/MDM and policy helper) are never declined, and a +// server force-on carrying `failIfUnavailable` is preserved so the session fails loudly +// instead of quietly running unsandboxed. Rejects when a supplied layer is present but is +// not a JSON object, rather than treating it as unmanaged, because silently dropping a +// malformed policy would hand the user's own layer the win on exactly the keys the policy +// exists to control. +// +// RPC method: managedSettings.resolveEffectiveValue. +// +// Parameters: The managed-settings layers to compose into an effective policy. Every layer +// is optional; omitting all of them yields no effective policy. +// +// Returns: The effective managed policy composed from the supplied layers. +// Internal: ResolveEffectiveValue is part of the SDK's internal handshake/plumbing; +// external callers should not use it. +func (a *InternalServerManagedSettingsAPI) ResolveEffectiveValue(ctx context.Context, params *ManagedSettingsResolveEffectiveValueParams) (*ManagedSettingsResolveEffectiveValueResult, error) { + raw, err := a.client.Request(ctx, "managedSettings.resolveEffectiveValue", params) + if err != nil { + return nil, err + } + var result ManagedSettingsResolveEffectiveValueResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// ResolveStartupPolicy resolves the managed (enterprise policy) settings a host must +// enforce from startup, and judges the account against the enterprise sign-in pin in the +// same pass. This composes the layers the way `managedSettings.ingest` does and then +// answers the two questions a host cannot answer for itself: which fail-closed floors apply +// when a layer could not be confirmed, and whether this credential is allowed to sign in at +// all. The sign-in verdict is the reason a host must not assemble this itself from +// `managedSettings.ingest` — a pinned organization is an access control, and a host that +// composed the layers but skipped the pin would let a developer sign in with a personal or +// another enterprise's account and escape every control that keys off the signed-in +// identity. The verdict is computed on every resolution, including one that is not +// retained, so declining to apply policy never widens access. Set `retainAndApply` to keep +// the resolved policy as this runtime process's startup policy; leave it false to resolve +// without side effects. +// +// RPC method: managedSettings.resolveStartupPolicy. +// +// Parameters: Inputs describing the host and account a startup policy resolution should run +// for. Mirrors `managedSettings.ingest`, and adds the decision of whether the resolved +// policy is retained for this runtime process. +// +// Returns: The startup policy a resolution composed, the per-layer outcome flags a host +// needs to decide how much to trust it, and the enterprise sign-in verdict for the +// credential it ran against. The flags are reported rather than folded into the settings +// because a host must distinguish an unmanaged device from a managed device whose policy +// could not be confirmed, and apply its fail-closed gates in the second case. +// Internal: ResolveStartupPolicy is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerManagedSettingsAPI) ResolveStartupPolicy(ctx context.Context, params *ManagedSettingsResolveStartupPolicyParams) (*ManagedSettingsResolveStartupPolicyResult, error) { + raw, err := a.client.Request(ctx, "managedSettings.resolveStartupPolicy", params) + if err != nil { + return nil, err + } + var result ManagedSettingsResolveStartupPolicyResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// RetainPolicy retains the supplied managed layers as this runtime process's startup policy +// and re-applies the result to every live session. Hosts that own their own short-lived +// process policy use this, so a guarded mutation is checked against the policy the host +// actually fetched. Retaining is a whole-process replacement rather than a merge, so a host +// that already resolved a good policy must not retain a partial one over it. The failure +// flags are honest inputs rather than assumptions: reporting a fetch or load that failed +// keeps the layers already retained, and the interim fail-closed state, instead of clearing +// them. +// +// RPC method: managedSettings.retainPolicy. +// +// Parameters: The managed layers to retain as this process's startup policy, with the +// outcome of fetching each one. +// Internal: RetainPolicy is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerManagedSettingsAPI) RetainPolicy(ctx context.Context, params *ManagedSettingsRetainPolicyParams) (*ManagedSettingsRetainPolicyResult, error) { + raw, err := a.client.Request(ctx, "managedSettings.retainPolicy", params) + if err != nil { + return nil, err + } + var result ManagedSettingsRetainPolicyResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: InternalServerMCPAPI contains experimental APIs that may change or be +// removed. +type InternalServerMCPAPI internalServerAPI + +// ListBuiltinServerNames lists the MCP servers the runtime supplies itself, so a host that +// disables the built-ins wholesale suppresses exactly the set the runtime would otherwise +// add. The names are fixed for a build and depend on no configuration, session or account. +// +// RPC method: mcp.listBuiltinServerNames. +// +// Returns: Names of the MCP servers the runtime supplies itself. +// Internal: ListBuiltinServerNames is part of the SDK's internal handshake/plumbing; +// external callers should not use it. +func (a *InternalServerMCPAPI) ListBuiltinServerNames(ctx context.Context) (*MCPListBuiltinServerNamesResult, error) { + raw, err := a.client.Request(ctx, "mcp.listBuiltinServerNames", nil) + if err != nil { + return nil, err + } + var result MCPListBuiltinServerNamesResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// ShouldExcludeGitHubTools reports whether the gh-replaceable GitHub MCP tools may be +// clipped for a session, which a host feeds back as the `excludeGhReplaceableTools` build +// option. The tools are redundant only when the session can reach a shell *and* the host +// has the `gh` that would replace them, so both halves are decided here. The shell half +// runs the platform shell tool through the runtime's own tool-filter matcher, so entry +// forms like `builtin:bash` and `builtin:*` behave exactly as they do when a session builds +// its tool catalog. The host half probes for `gh`, and is skipped entirely when the filters +// already rule the shell out. Host presence alone is not enough: a session restricted to, +// say, `view` would otherwise lose the built-in issue-read fallback while having no shell +// to replace it with. The answer describes the host the runtime runs on, so it is never +// forwarded to a remote engine. +// +// RPC method: mcp.shouldExcludeGitHubTools. +// +// Parameters: The session tool filters that decide whether the session still has a shell to +// run `gh` with. +// +// Returns: Whether the gh-replaceable GitHub MCP tools may be clipped for the session +// described by the request. +// Internal: ShouldExcludeGitHubTools is part of the SDK's internal handshake/plumbing; +// external callers should not use it. +func (a *InternalServerMCPAPI) ShouldExcludeGitHubTools(ctx context.Context, params *MCPShouldExcludeGitHubToolsRequest) (*MCPShouldExcludeGitHubToolsResult, error) { + raw, err := a.client.Request(ctx, "mcp.shouldExcludeGitHubTools", params) + if err != nil { + return nil, err + } + var result MCPShouldExcludeGitHubToolsResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: InternalServerMCPRegistryAPI contains experimental APIs that may change or +// be removed. +type InternalServerMCPRegistryAPI internalServerAPI + +// AllocateRequestId allocates an ID for one cancellable MCP registry search. The ID exists +// before the search starts, so callers can cancel before it starts. The networking stack +// supplies the cancellation namespace. The runtime retains at most 1,024 unused IDs. At +// capacity, another allocation can reclaim an unused ID. Active searches retain their IDs +// until they finish. +// +// RPC method: mcp.registry.allocateRequestId. +// +// Returns: Request id naming a cancellable registry search. +// Internal: AllocateRequestId is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerMCPRegistryAPI) AllocateRequestId(ctx context.Context) (*MCPRegistryRequestIDResult, error) { + raw, err := a.client.Request(ctx, "mcp.registry.allocateRequestId", nil) + if err != nil { + return nil, err + } + var result MCPRegistryRequestIDResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Cancel abandons the registry search that uses the given request ID. It acts only on IDs +// from `mcp.registry.allocateRequestId`, so it never cancels another component's request. +// Answers `canceled: true` when it stops a running search. Answers `canceled: false` for +// unknown or reclaimed IDs, completed or canceled searches, and unused reservations. It +// releases an unused reservation, so a later search with that ID is refused. +// +// RPC method: mcp.registry.cancel. +// +// Parameters: Registry search to abandon. +// +// Returns: Whether the cancel reached a live search. +// Internal: Cancel is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerMCPRegistryAPI) Cancel(ctx context.Context, params *MCPRegistryCancelRequest) (*MCPRegistryCancelResult, error) { + raw, err := a.client.Request(ctx, "mcp.registry.cancel", params) + if err != nil { + return nil, err + } + var result MCPRegistryCancelResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Searches the MCP registry the supplied credential may read, resolving the registry +// endpoint from policy first. Hosts usually send credential-free `AuthIdentity`; a `token` +// identity can be resolved only when it embeds a token, while `env` and `gh-cli` identities +// can use a token embedded in the request first. The runtime follows registry pages, keeps +// the newest entry per server name, cuts the list to `limit`, and sorts an empty-query +// result by GitHub stars. Each server object is carried opaquely. +// +// RPC method: mcp.registry.search. +// +// Parameters: Registry search terms, the credential to search under, and the request id +// that makes the search cancellable. +// +// Returns: Servers selected from the registry response. +// Internal: Search is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerMCPRegistryAPI) Search(ctx context.Context, params *MCPRegistrySearchRequest) (*MCPRegistrySearchResult, error) { + raw, err := a.client.Request(ctx, "mcp.registry.search", params) + if err != nil { + return nil, err + } + var result MCPRegistrySearchResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: Registry returns experimental APIs that may change or be removed. +func (s *InternalServerMCPAPI) Registry() *InternalServerMCPRegistryAPI { + return (*InternalServerMCPRegistryAPI)(s) +} + +// Experimental: InternalServerPermissionsAPI contains experimental APIs that may change or +// be removed. +type InternalServerPermissionsAPI internalServerAPI + +// EvaluateManagedPolicy interprets a verbatim managed `permissions` slice into the three +// policy questions a host asks before granting elevated permissions: whether +// bypass-permissions mode is forbidden, whether a policy exists that this build could not +// determine, and whether the layer supplies permission rules the session must enforce. An +// absent or empty slice means no managed policy applies. The runtime and its permission +// service share one policy type, so a new managed permission setting is interpreted in +// exactly one place; a host that reimplements the reading would silently diverge the first +// time that set grows. Hosts must fail closed on an undetermined policy rather than +// treating it as permission granted. +// +// RPC method: permissions.evaluateManagedPolicy. +// +// Parameters: The verbatim managed `permissions` slice to interpret. +// +// Returns: The policy questions a host asks before granting elevated permissions. +// Internal: EvaluateManagedPolicy is part of the SDK's internal handshake/plumbing; +// external callers should not use it. +func (a *InternalServerPermissionsAPI) EvaluateManagedPolicy(ctx context.Context, params *PermissionsEvaluateManagedPolicyParams) (*PermissionsEvaluateManagedPolicyResult, error) { + raw, err := a.client.Request(ctx, "permissions.evaluateManagedPolicy", params) + if err != nil { + return nil, err + } + var result PermissionsEvaluateManagedPolicyResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: InternalServerPermissionsFolderTrustAPI contains experimental APIs that may +// change or be removed. +type InternalServerPermissionsFolderTrustAPI internalServerAPI + +// CheckTrust reports whether a folder is trusted, reading the persisted trusted-folders +// list directly rather than through a session. Use this from a host that has no session +// yet, such as a one-shot command deciding whether to honor a checkout's own settings. It +// differs from the session-scoped check in two ways that matter: it takes the configuration +// directory to read the list from, instead of inheriting the session's, and it answers only +// from the persisted list, so trust a user granted for the lifetime of one session is not +// visible here. A host that has a session should prefer the session-scoped check, which +// also covers session-only trust. +// +// RPC method: permissions.folderTrust.checkTrust. +// +// Parameters: A folder to check against a persisted trusted-folders list, and the +// configuration directory holding that list. +// +// Returns: Folder trust check result. +// Internal: CheckTrust is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerPermissionsFolderTrustAPI) CheckTrust(ctx context.Context, params *FolderTrustIsTrustedParams) (*FolderTrustCheckResult, error) { + raw, err := a.client.Request(ctx, "permissions.folderTrust.checkTrust", params) + if err != nil { + return nil, err + } + var result FolderTrustCheckResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: FolderTrust returns experimental APIs that may change or be removed. +func (s *InternalServerPermissionsAPI) FolderTrust() *InternalServerPermissionsFolderTrustAPI { + return (*InternalServerPermissionsFolderTrustAPI)(s) +} + +// Experimental: InternalServerPermissionsLocationsAPI contains experimental APIs that may +// change or be removed. +type InternalServerPermissionsLocationsAPI internalServerAPI + +// CleanupOrphaned drops stored per-location permission grants whose directory no longer +// exists, and reports which location keys were removed. Runs against the permission store +// under the runtime's own Copilot home, the same store +// `session.permissions.locations.resolve` reads. Marked internal because it exists to carry +// a CLI call site off the napi boundary onto the SDK contract; it is migration plumbing, +// not a surface consumers are meant to depend on. It can become public once an SDK consumer +// needs to prune its own permission store. +// +// RPC method: permissions.locations.cleanupOrphaned. +// +// Returns: Which stored location permissions were dropped because their directory is gone. +// Internal: CleanupOrphaned is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerPermissionsLocationsAPI) CleanupOrphaned(ctx context.Context) (*LocationPermissionsCleanupResult, error) { + raw, err := a.client.Request(ctx, "permissions.locations.cleanupOrphaned", nil) + if err != nil { + return nil, err + } + var result LocationPermissionsCleanupResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: Locations returns experimental APIs that may change or be removed. +func (s *InternalServerPermissionsAPI) Locations() *InternalServerPermissionsLocationsAPI { + return (*InternalServerPermissionsLocationsAPI)(s) +} + +// Experimental: InternalServerPersistenceAPI contains experimental APIs that may change or +// be removed. +type InternalServerPersistenceAPI internalServerAPI + +// WriteRawJsonLocked atomically replaces a JSON file's entire contents while holding the +// runtime's per-path lock. The lock lives in the runtime process, so a host that writes +// these files itself takes no part in it and can tear a concurrent runtime write; route the +// write here instead. Unlike a merging write this never reads or parses the existing file, +// so a previously corrupted snapshot is replaced wholesale rather than failing every later +// write. Use it only for callers that always persist a complete snapshot. +// +// RPC method: persistence.writeRawJsonLocked. +// +// Parameters: A complete JSON snapshot to write under the runtime's per-path file lock. +// +// Returns: The outcome of a locked snapshot write. +// Internal: WriteRawJsonLocked is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerPersistenceAPI) WriteRawJsonLocked(ctx context.Context, params *PersistenceWriteRawJSONLockedRequest) (*PersistenceWriteRawJSONLockedResult, error) { + raw, err := a.client.Request(ctx, "persistence.writeRawJsonLocked", params) + if err != nil { + return nil, err + } + var result PersistenceWriteRawJSONLockedResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: InternalServerRepoAPI contains experimental APIs that may change or be +// removed. +type InternalServerRepoAPI internalServerAPI + +// Experimental: InternalServerRepoSettingsAPI contains experimental APIs that may change or +// be removed. +type InternalServerRepoSettingsAPI internalServerAPI + +// Edit atomically edits one allowed top-level repository setting. Reads the raw file, +// preserves unrelated keys, applies one scalar or list edit, validates the result and +// writes under one file lock, so concurrent sibling or list edits are not lost. A malformed +// or unsafe existing file returns `invalid` without a write; rejected keys, invalid values +// and I/O errors reject the call. +// +// RPC method: repo.settings.edit. +// +// Parameters: One atomic edit of an allowed top-level repository setting. +// +// Returns: Outcome of an atomic user or repository settings edit. +// Internal: Edit is part of the SDK's internal handshake/plumbing; external callers should +// not use it. +func (a *InternalServerRepoSettingsAPI) Edit(ctx context.Context, params *RepoSettingsEditRequest) (*SettingsEditResult, error) { + raw, err := a.client.Request(ctx, "repo.settings.edit", params) + if err != nil { + return nil, err + } + var result SettingsEditResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// ReadRaw reads one repository settings file without settings normalization or layering. +// The existing JSONC parser accepts comments and trailing commas and applies its +// prototype-safety rules. Missing or blank files return an empty valid config. Invalid +// files return an empty config with invalid=true; other read failures reject. +// +// RPC method: repo.settings.readRaw. +// +// Parameters: Which repository settings file to read. +// +// Returns: Parsed repository settings without normalization or layering. +// Internal: ReadRaw is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerRepoSettingsAPI) ReadRaw(ctx context.Context, params *RepoSettingsReadRawRequest) (*RepoSettingsReadRawResult, error) { + raw, err := a.client.Request(ctx, "repo.settings.readRaw", params) + if err != nil { + return nil, err + } + var result RepoSettingsReadRawResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// WriteAll replaces the repository-overridable settings in one locked write. Allowed keys +// present in settings are validated and written; allowed keys absent from settings are +// removed. Other input keys are ignored, and unknown keys already stored in the file remain +// unchanged. Invalid values or an invalid existing file reject without a partial write. +// +// RPC method: repo.settings.writeAll. +// +// Parameters: The complete set of repository-overridable settings to store. +// Internal: WriteAll is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerRepoSettingsAPI) WriteAll(ctx context.Context, params *RepoSettingsWriteAllRequest) (*RepoSettingsWriteAllResult, error) { + raw, err := a.client.Request(ctx, "repo.settings.writeAll", params) + if err != nil { + return nil, err + } + var result RepoSettingsWriteAllResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// WriteKey writes or removes one allowed top-level repository setting through the existing +// validator and file lock. Preserves unrelated stored keys. Invalid keys, values, existing +// documents, and I/O errors reject without replacing the file. Deletion requires +// delete=true; otherwise value must be present. +// +// RPC method: repo.settings.writeKey. +// +// Parameters: One key to write or remove in a repository settings file. +// Internal: WriteKey is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerRepoSettingsAPI) WriteKey(ctx context.Context, params *RepoSettingsWriteKeyRequest) (*RepoSettingsWriteKeyResult, error) { + raw, err := a.client.Request(ctx, "repo.settings.writeKey", params) + if err != nil { + return nil, err + } + var result RepoSettingsWriteKeyResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: Settings returns experimental APIs that may change or be removed. +func (s *InternalServerRepoAPI) Settings() *InternalServerRepoSettingsAPI { + return (*InternalServerRepoSettingsAPI)(s) +} + +// Experimental: InternalServerSandboxAPI contains experimental APIs that may change or be +// removed. +type InternalServerSandboxAPI internalServerAPI + +// CheckEnforcementCommand whether one slash command stays reachable while sandbox +// enforcement is unavailable and the session is otherwise blocked. The permitted set is +// deliberately narrow — commands that only report state, and the few that let the user +// repair or leave the situation — and it is keyed on the arguments as well as the name, so +// `/sandbox status` is admitted where `/sandbox enable` is not. A host must ask rather than +// keep its own list: a command this returns false for is one the runtime would refuse +// anyway, and a divergent list either strands the user with no way out or admits a command +// that acts on an unenforced session. +// +// RPC method: sandbox.checkEnforcementCommand. +// +// Parameters: One slash command, as typed, to test against the enforcement-block allowlist. +// +// Returns: True when the command stays available while enforcement is blocked. +// Internal: CheckEnforcementCommand is part of the SDK's internal handshake/plumbing; +// external callers should not use it. +func (a *InternalServerSandboxAPI) CheckEnforcementCommand(ctx context.Context, params *SandboxEnforcementCommandAllowedParams) (*SandboxCheckEnforcementCommandResult, error) { + raw, err := a.client.Request(ctx, "sandbox.checkEnforcementCommand", params) + if err != nil { + return nil, err + } + var result SandboxCheckEnforcementCommandResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// CheckPlatformSupport reports whether this machine can run an OS-level command sandbox at +// all. A host must ask before it offers sandboxing, because the answer is a property of the +// machine rather than of any setting: on Linux it depends on bubblewrap being installed, +// and on Windows on the OS build supporting BaseContainer. Probing it requires spawning a +// process the first time, so the answer is cached for the life of the runtime. A host that +// assumes support and enables the sandbox anyway produces a session in which every shell +// command fails. +// +// RPC method: sandbox.checkPlatformSupport. +// +// Returns: True when an OS-level sandbox can run on this host. +// Internal: CheckPlatformSupport is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerSandboxAPI) CheckPlatformSupport(ctx context.Context) (*SandboxCheckPlatformSupportResult, error) { + raw, err := a.client.Request(ctx, "sandbox.checkPlatformSupport", nil) + if err != nil { + return nil, err + } + var result SandboxCheckPlatformSupportResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// CheckWindowsHostLoopbackSupport reports whether this Windows host's BaseContainer +// implementation can reach the host's loopback interface directly, which is what a +// local-network policy needs to avoid routing through the egress proxy. Like +// `sandbox.checkPlatformSupport`, the answer is a property of the machine's OS build rather +// than of any setting, and resolving it the first time spawns a platform probe. A host that +// offers the direct local-network option on a machine that lacks the capability produces a +// session whose sandboxed commands cannot reach local services. Always false off Windows. +// +// RPC method: sandbox.checkWindowsHostLoopbackSupport. +// +// Returns: True when the sandbox on this host can reach host loopback directly. +// Internal: CheckWindowsHostLoopbackSupport is part of the SDK's internal +// handshake/plumbing; external callers should not use it. +func (a *InternalServerSandboxAPI) CheckWindowsHostLoopbackSupport(ctx context.Context) (*SandboxCheckWindowsHostLoopbackSupportResult, error) { + raw, err := a.client.Request(ctx, "sandbox.checkWindowsHostLoopbackSupport", nil) + if err != nil { + return nil, err + } + var result SandboxCheckWindowsHostLoopbackSupportResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// FilterPickerCandidates checks a batch of `@`-file picker suggestions against a policy +// prepared by `sandbox.preparePickerPolicy` and returns one allow decision per candidate, +// positionally aligned with the request. Batched deliberately: each check canonicalizes a +// path component-by-component through the real filesystem, so one call amortizes that over +// a single hop rather than dozens while the user types. A directory the policy does not +// itself allow can still be kept as a waypoint to an allowed subtree. An absent policy +// keeps every candidate. +// +// RPC method: sandbox.filterPickerCandidates. +// +// Parameters: A batch of file-picker candidates to check against a prepared policy. +// +// Returns: Per-candidate allow decisions, positionally aligned with the request. +// Internal: FilterPickerCandidates is part of the SDK's internal handshake/plumbing; +// external callers should not use it. +func (a *InternalServerSandboxAPI) FilterPickerCandidates(ctx context.Context, params *SandboxFilterPickerCandidatesParams) (*SandboxFilterPickerCandidatesResult, error) { + raw, err := a.client.Request(ctx, "sandbox.filterPickerCandidates", params) + if err != nil { + return nil, err + } + var result SandboxFilterPickerCandidatesResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetDocsUrl returns the documentation URL a host should point a user at when it declines +// or downgrades a sandbox setting. The runtime owns the address so every surface cites the +// same page and a host does not hard-code a link that later moves. +// +// RPC method: sandbox.getDocsUrl. +// +// Returns: The documentation URL a host cites when it declines or downgrades a sandbox +// setting. +// Internal: GetDocsUrl is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerSandboxAPI) GetDocsUrl(ctx context.Context) (*SandboxDocsURLResult, error) { + raw, err := a.client.Request(ctx, "sandbox.getDocsUrl", nil) + if err != nil { + return nil, err + } + var result SandboxDocsURLResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetUnsupportedMessage the explanation a host shows when it refuses an explicit request to +// turn the sandbox on for a machine that cannot run one. Carries the platform-specific +// remedy — install bubblewrap, update Windows — and the documentation link, so a host that +// got `false` from `sandbox.checkPlatformSupport` does not have to compose its own reason +// and cannot describe the same machine differently from the runtime's own spawn path. +// +// RPC method: sandbox.getUnsupportedMessage. +// +// Returns: The user-facing refusal, including the platform remedy and the documentation +// link. +// Internal: GetUnsupportedMessage is part of the SDK's internal handshake/plumbing; +// external callers should not use it. +func (a *InternalServerSandboxAPI) GetUnsupportedMessage(ctx context.Context) (*SandboxGetUnsupportedMessageResult, error) { + raw, err := a.client.Request(ctx, "sandbox.getUnsupportedMessage", nil) + if err != nil { + return nil, err + } + var result SandboxGetUnsupportedMessageResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetUnsupportedSettingDisabledNotice the notice a host shows when a stored `sandbox` +// setting is being ignored for this session because the machine cannot enforce it. Distinct +// from `sandbox.getUnsupportedMessage`, which refuses an explicit request: this one reports +// that a setting the user already saved is inert, states that commands will run +// unsandboxed, and confirms the stored setting itself is left alone. A host that stays +// silent here leaves the user believing a sandbox is active. +// +// RPC method: sandbox.getUnsupportedSettingDisabledNotice. +// +// Returns: The user-facing notice that a saved sandbox setting is inert for this session. +// Internal: GetUnsupportedSettingDisabledNotice is part of the SDK's internal +// handshake/plumbing; external callers should not use it. +func (a *InternalServerSandboxAPI) GetUnsupportedSettingDisabledNotice(ctx context.Context) (*SandboxGetUnsupportedSettingDisabledNoticeResult, error) { + raw, err := a.client.Request(ctx, "sandbox.getUnsupportedSettingDisabledNotice", nil) + if err != nil { + return nil, err + } + var result SandboxGetUnsupportedSettingDisabledNoticeResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetUnsupportedStartupNotice the warning a host prints at startup when a session asked for +// a sandbox on a machine that cannot run one. The third of the unsupported notices, and the +// only one that depends on the policy in force: it names the remedy the reader can actually +// act on, which is the stored setting for an unmanaged host, an administrator for a managed +// one, and waiting for the policy to resolve when the sandbox stayed on only because the +// policy could not be determined. Returns no notice when the machine does support a +// sandbox, so a host can call it unconditionally on the startup path. A host that composes +// its own warning tells a managed user to change a setting their policy will overwrite. +// +// RPC method: sandbox.getUnsupportedStartupNotice. +// +// Parameters: The managed sandbox policy in force, which selects which remedy the notice +// offers. +// +// Returns: The startup warning, absent when the machine can run a sandbox. +// Internal: GetUnsupportedStartupNotice is part of the SDK's internal handshake/plumbing; +// external callers should not use it. +func (a *InternalServerSandboxAPI) GetUnsupportedStartupNotice(ctx context.Context, params *SandboxUnsupportedStartupNoticeParams) (*SandboxUnsupportedStartupNoticeResult, error) { + raw, err := a.client.Request(ctx, "sandbox.getUnsupportedStartupNotice", params) + if err != nil { + return nil, err + } + var result SandboxUnsupportedStartupNoticeResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// PreparePickerPolicy builds the effective filesystem policy one `@`-file picker checks its +// suggestions against, so Git discovery and grant canonicalization run once for the +// picker's lifetime instead of on every debounced keystroke. Returns no policy when the +// sandbox is absent or disabled, which the paired filter reads as "keep every candidate". A +// host must pair this with `sandbox.filterPickerCandidates` rather than resolve a grant +// list itself, because the picker's grants are narrower than a file tool's — the same path +// a built-in tool may read can be one the picker must not offer. +// +// RPC method: sandbox.preparePickerPolicy. +// +// Parameters: The working directory and sandbox settings to build a file-picker policy from. +// +// Returns: The prepared file-picker policy, or none when the sandbox is disabled. +// Internal: PreparePickerPolicy is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerSandboxAPI) PreparePickerPolicy(ctx context.Context, params *SandboxPreparePickerPolicyParams) (*SandboxPreparePickerPolicyResult, error) { + raw, err := a.client.Request(ctx, "sandbox.preparePickerPolicy", params) + if err != nil { + return nil, err + } + var result SandboxPreparePickerPolicyResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// ResolveFlooredConfig raises a user's sandbox settings to an administrator's managed +// sandbox floor and returns the result a session should actually run under. The floor can +// only tighten: an enterprise force-on is reflected even when the user's own `enabled` is +// false, and a user cannot lower a setting the floor raised. The result is normalized +// through the declared sandbox shape, so a key reorder or an undeclared passthrough key is +// not mistaken for a policy change by callers that diff two results. A host must call this +// rather than merge the two layers itself, because reimplementing the direction of the fold +// is what turns an administrator's floor into a suggestion. +// +// RPC method: sandbox.resolveFlooredConfig. +// +// Parameters: The user's sandbox settings and the managed sandbox floor to raise them to. +// +// Returns: The user's sandbox settings raised to the managed floor. +// Internal: ResolveFlooredConfig is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerSandboxAPI) ResolveFlooredConfig(ctx context.Context, params *SandboxResolveFlooredConfigParams) (*SandboxResolveFlooredConfigResult, error) { + raw, err := a.client.Request(ctx, "sandbox.resolveFlooredConfig", params) + if err != nil { + return nil, err + } + var result SandboxResolveFlooredConfigResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// ResolveForOptional the sandbox a single named feature actually launches under, which is +// not the same as the session's sandbox. A feature is routed out of the sandbox by its own +// flag — `sandboxMcpServers`, `sandboxLspServers` — so an enabled session can still start +// one server confined and another free. Returns a disabled sandbox rather than an error +// when the host has no sandbox slice at all, so an absent config and an opted-out feature +// read alike at the launch site. A host must ask per feature rather than reuse the session +// config, because reading only `enabled` confines a server the user opted out of, or +// reports as sandboxed one that is not. +// +// RPC method: sandbox.resolveForOptional. +// +// Parameters: A sandbox config and the per-feature routing flag to resolve it for. +// +// Returns: The sandbox a named feature actually launches under. +// Internal: ResolveForOptional is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerSandboxAPI) ResolveForOptional(ctx context.Context, params *SandboxEffectiveForOptionalParams) (*SandboxEffectiveForOptionalResult, error) { + raw, err := a.client.Request(ctx, "sandbox.resolveForOptional", params) + if err != nil { + return nil, err + } + var result SandboxEffectiveForOptionalResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: InternalServerSandboxProxySecretAPI contains experimental APIs that may +// change or be removed. +type InternalServerSandboxProxySecretAPI internalServerAPI + +// Delete removes the stored sandbox proxy password from the credential store +// `sandbox.proxySecret.getLocation` names. Succeeds when no entry exists, so it is safe to +// call while clearing a proxy password whose origin is unknown. Marked internal because it +// exists to carry a CLI call site off the napi boundary onto the SDK contract. +// +// RPC method: sandbox.proxySecret.delete. +// Internal: Delete is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerSandboxProxySecretAPI) Delete(ctx context.Context) (*SandboxProxySecretDeleteResult, error) { + raw, err := a.client.Request(ctx, "sandbox.proxySecret.delete", nil) + if err != nil { + return nil, err + } + var result SandboxProxySecretDeleteResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Get reads the stored sandbox proxy password from the credential store +// `sandbox.proxySecret.getLocation` names, preferring the OS keychain and falling back to +// the on-disk store. Reports an absent value rather than failing when no entry exists. +// Marked internal because it exists to carry a CLI call site off the napi boundary onto the +// SDK contract. +// +// RPC method: sandbox.proxySecret.get. +// +// Returns: The stored sandbox proxy password, if one is present. +// Internal: Get is part of the SDK's internal handshake/plumbing; external callers should +// not use it. +func (a *InternalServerSandboxProxySecretAPI) Get(ctx context.Context) (*SandboxProxySecretGetResult, error) { + raw, err := a.client.Request(ctx, "sandbox.proxySecret.get", nil) + if err != nil { + return nil, err + } + var result SandboxProxySecretGetResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetLocation reports where this runtime keeps the sandbox proxy password: the credential +// store directory and the entry identifier within it. Both are derived from the +// configuration directory this runtime was started with, so a host does not resolve them +// itself. Read this to recognize a `${secret:…}` value in stored sandbox settings as this +// runtime's own proxy password rather than a reference the user wrote by hand. Marked +// internal because it exists to carry a CLI call site off the napi boundary onto the SDK +// contract. +// +// RPC method: sandbox.proxySecret.getLocation. +// +// Returns: Where this runtime keeps the sandbox proxy password. +// Internal: GetLocation is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerSandboxProxySecretAPI) GetLocation(ctx context.Context) (*SandboxProxySecretLocation, error) { + raw, err := a.client.Request(ctx, "sandbox.proxySecret.getLocation", nil) + if err != nil { + return nil, err + } + var result SandboxProxySecretLocation + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Save stores the sandbox proxy password in the credential store +// `sandbox.proxySecret.getLocation` names, replacing any existing entry. Reports whether +// the OS keychain accepted it or it fell back to the on-disk store. Marked internal because +// it exists to carry a CLI call site off the napi boundary onto the SDK contract. +// +// RPC method: sandbox.proxySecret.save. +// +// Parameters: The sandbox proxy password to store. +// +// Returns: Where the sandbox proxy password was stored. +// Internal: Save is part of the SDK's internal handshake/plumbing; external callers should +// not use it. +func (a *InternalServerSandboxProxySecretAPI) Save(ctx context.Context, params *SandboxProxySecretSaveRequest) (*SandboxProxySecretSaveResult, error) { + raw, err := a.client.Request(ctx, "sandbox.proxySecret.save", params) + if err != nil { + return nil, err + } + var result SandboxProxySecretSaveResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: ProxySecret returns experimental APIs that may change or be removed. +func (s *InternalServerSandboxAPI) ProxySecret() *InternalServerSandboxProxySecretAPI { + return (*InternalServerSandboxProxySecretAPI)(s) +} + +// Experimental: InternalServerSandboxSettingsAPI contains experimental APIs that may change +// or be removed. +type InternalServerSandboxSettingsAPI internalServerAPI + +// ApplyDefaults resolves a stored sandbox settings object into the effective one, filling +// every value the user left unset with its default. Read this to render or reason about the +// sandbox configuration a session would actually run under, rather than the sparse document +// the user wrote. +// +// RPC method: sandbox.settings.applyDefaults. +// +// Parameters: The stored sandbox settings to resolve against the defaults. +// +// Returns: The effective sandbox settings, with every unset value resolved to its default. +// Internal: ApplyDefaults is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerSandboxSettingsAPI) ApplyDefaults(ctx context.Context, params *SandboxApplyDefaultsRequest) (*SandboxSettingsApplyDefaultsResult, error) { + raw, err := a.client.Request(ctx, "sandbox.settings.applyDefaults", params) + if err != nil { + return nil, err + } + var result SandboxSettingsApplyDefaultsResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: Settings returns experimental APIs that may change or be removed. +func (s *InternalServerSandboxAPI) Settings() *InternalServerSandboxSettingsAPI { + return (*InternalServerSandboxSettingsAPI)(s) +} + // Experimental: InternalServerSessionsAPI contains experimental APIs that may change or be // removed. type InternalServerSessionsAPI internalServerAPI @@ -31402,19 +35562,707 @@ func (a *InternalServerSessionsAPI) UpdateWorkspaceFields(ctx context.Context, p return &result, nil } +// Experimental: InternalServerUserAPI contains experimental APIs that may change or be +// removed. +type InternalServerUserAPI internalServerAPI + +// Experimental: InternalServerUserSettingsAPI contains experimental APIs that may change or +// be removed. +type InternalServerUserSettingsAPI internalServerAPI + +// ApplyTopKey validates one top-level setting subtree and writes just that key to +// settings.json as a single step. Doing both here closes the race a caller cannot close +// itself: between a separate preflight read and the write, the file may change. Rewriting +// only the touched key keeps legacy siblings out of settings.json. Answers a status rather +// than failing, so a validation problem and a write problem stay distinguishable. +// +// RPC method: user.settings.applyTopKey. +// +// Parameters: A single top-level setting subtree to validate and write. +// +// Returns: Outcome of validating and writing one top-level setting subtree. +// Internal: ApplyTopKey is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerUserSettingsAPI) ApplyTopKey(ctx context.Context, params *UserSettingsApplyTopKeyRequest) (*UserSettingsApplyTopKeyResult, error) { + raw, err := a.client.Request(ctx, "user.settings.applyTopKey", params) + if err != nil { + return nil, err + } + var result UserSettingsApplyTopKeyResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// BuildRuntime resolves the effective runtime settings for a host that is about to start a +// session, layering the given overrides over the settings documents, the environment and +// the platform defaults. A host cannot assemble this itself: the precedence between a user +// document, a repository document, the managed layers and the environment is the runtime's +// own rule, and a host that re-implements it drifts from the server it is configuring. The +// working directory, environment and platform are taken as arguments rather than read from +// the server process, so a host resolving settings for a directory other than its own gets +// that directory's answer. Nothing is written; this only computes. +// +// RPC method: user.settings.buildRuntime. +// +// Parameters: The overrides and host context to resolve effective runtime settings from. +// +// Returns: The effective runtime settings, keyed by setting name. Keys this runtime does +// not recognize are preserved rather than dropped, so a caller reading a document written +// by a newer runtime keeps every value. +// Internal: BuildRuntime is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerUserSettingsAPI) BuildRuntime(ctx context.Context, params *UserSettingsBuildRuntimeRequest) (*UserSettingsBuildRuntimeResult, error) { + raw, err := a.client.Request(ctx, "user.settings.buildRuntime", params) + if err != nil { + return nil, err + } + var result UserSettingsBuildRuntimeResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// DiagnoseProblems diagnoses a set of already-loaded settings layers, reporting each key +// that will not take effect and why. Settings load tolerantly so that one bad key does not +// stop a session from starting; this method surfaces what was dropped so a caller can +// report it instead of silently ignoring the user's intent. Takes the layers as given +// rather than reading them, so a caller can diagnose exactly the content it is showing, +// including unsaved edits. Never echoes a setting's value, because a value may be a token +// or another secret. Marked internal because it exists to carry a CLI call site off the +// native addon, not to serve a consumer-facing need; it becomes public once an SDK consumer +// needs to inspect settings health itself. +// +// RPC method: user.settings.diagnoseProblems. +// +// Parameters: The settings layers to diagnose. +// +// Returns: The settings that will not take effect across the given layers. +// Internal: DiagnoseProblems is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerUserSettingsAPI) DiagnoseProblems(ctx context.Context, params *UserSettingsProblemsRequest) (*UserSettingsProblemsResult, error) { + raw, err := a.client.Request(ctx, "user.settings.diagnoseProblems", params) + if err != nil { + return nil, err + } + var result UserSettingsProblemsResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Edit atomically edits one canonical user setting inside its settings-file lock. Reads, +// canonicalizes touched aliases, applies the edit, validates the resulting top-level +// setting and writes before releasing the lock. An explicit autoUpdatesChannel set +// completes the staff migration in the same transaction. Only a malformed existing file +// returns `invalid`; refused keys, invalid values and I/O errors reject the call. +// +// RPC method: user.settings.edit. +// +// Parameters: One atomic edit of a canonical setting in settings.json. +// +// Returns: Outcome of an atomic user or repository settings edit. +// Internal: Edit is part of the SDK's internal handshake/plumbing; external callers should +// not use it. +func (a *InternalServerUserSettingsAPI) Edit(ctx context.Context, params *UserSettingsEditRequest) (*SettingsEditResult, error) { + req := map[string]any{} + if params != nil { + req["canonicalPath"] = params.CanonicalPath + if params.ConfigDir != nil { + req["configDir"] = *params.ConfigDir + } + req["operation"] = params.Operation + if params.Value != nil { + req["value"] = params.Value + } + if params.Operation != SettingsEditOperationRemove { + if _, ok := req["value"]; !ok { + req["value"] = nil + } + } + } + raw, err := a.client.Request(ctx, "user.settings.edit", req) + if err != nil { + return nil, err + } + var result SettingsEditResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetPath reports the on-disk path of the settings document `user.settings.load` and +// `user.settings.set` read and write. Resolved the same way those methods resolve it, so a +// host that shows or opens the settings file names the file the runtime actually uses, +// including the extensionless legacy `settings` file when that is what is present. +// +// RPC method: user.settings.getPath. +// +// Returns: The on-disk location of the user's settings document. +// Internal: GetPath is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerUserSettingsAPI) GetPath(ctx context.Context) (*UserSettingsPathResult, error) { + raw, err := a.client.Request(ctx, "user.settings.getPath", nil) + if err != nil { + return nil, err + } + var result UserSettingsPathResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetPaths resolves the on-disk locations of the settings files this runtime reads, so a +// consumer can show or open them. The user path is the same file `user.settings.get`, +// `user.settings.load`, and `user.settings.set` operate on. Supply `repoRoot` to also +// resolve that repository's checked-in and local settings paths. A path is reported whether +// or not the file exists. +// +// RPC method: user.settings.getPaths. +// +// Parameters: Selects which settings file paths to resolve. +// +// Returns: On-disk locations of the settings files this runtime reads. A path is reported +// whether or not the file exists. +// Internal: GetPaths is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerUserSettingsAPI) GetPaths(ctx context.Context, params *UserSettingsGetPathsRequest) (*UserSettingsGetPathsResult, error) { + raw, err := a.client.Request(ctx, "user.settings.getPaths", params) + if err != nil { + return nil, err + } + var result UserSettingsGetPathsResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetUnknownKeysWarning reports the top-level keys in the user settings document that the +// runtime does not recognize, phrased for display to the user. This is a separate question +// from the warning `user.settings.loadWithWarning` returns: that one describes parts of the +// document the loader had to ignore to load the rest, whereas an unknown key parses cleanly +// and is simply not a setting, so a host that surfaces only the load warning silently +// accepts a misspelled key that will never take effect. Nothing is reported when the +// document is absent, unreadable, or failed to load, because an unknown-key list derived +// from a document the runtime could not read would be misleading rather than helpful. +// Inspection never modifies the document. +// +// RPC method: user.settings.getUnknownKeysWarning. +// +// Parameters: Selects the configuration directory whose user settings document should be +// inspected for unrecognized top-level keys. +// +// Returns: The unrecognized top-level keys found in the user settings document, already +// phrased for display. +// Internal: GetUnknownKeysWarning is part of the SDK's internal handshake/plumbing; +// external callers should not use it. +func (a *InternalServerUserSettingsAPI) GetUnknownKeysWarning(ctx context.Context, params *UserSettingsUnknownKeysWarningRequest) (*UserSettingsUnknownKeysWarningResult, error) { + raw, err := a.client.Request(ctx, "user.settings.getUnknownKeysWarning", params) + if err != nil { + return nil, err + } + var result UserSettingsUnknownKeysWarningResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// ListGovernanceKeys lists the top-level user-setting keys by who is allowed to govern +// them: `repo` keys a repository may override through `.github/copilot/settings.json`, +// `claudeRepo` keys a repository may override through a Claude-compatible config, and +// `managed` keys an enterprise policy may fix. The lists are static for a given runtime +// build and fail closed: a key absent from a list cannot be governed that way. +// +// RPC method: user.settings.listGovernanceKeys. +// +// Returns: Top-level user-setting keys grouped by who may govern them. +// Internal: ListGovernanceKeys is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerUserSettingsAPI) ListGovernanceKeys(ctx context.Context) (*UserSettingsGovernanceKeysResult, error) { + raw, err := a.client.Request(ctx, "user.settings.listGovernanceKeys", nil) + if err != nil { + return nil, err + } + var result UserSettingsGovernanceKeysResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// LoadAndMergeRepo reads a repository's settings and merges them over a supplied user layer +// in one step, returning the settings a consumer should act on inside that repository. Use +// this rather than reading the two layers separately: the merge is per setting and +// collection-aware, so a repository adds to the collections it is allowed to influence +// without replacing the user's own entries, and a whole-document overlay would get that +// wrong. Any diagnostic raised while reading the repository layer is returned rather than +// printed, so the consumer decides how to surface it; the settings that did load are merged +// regardless. +// +// RPC method: user.settings.loadAndMergeRepo. +// +// Parameters: A repository to read settings from, and the user layer they are merged over. +// +// Returns: The settings a consumer should act on inside a repository, together with any +// diagnostic raised while reading the repository layer. +// Internal: LoadAndMergeRepo is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerUserSettingsAPI) LoadAndMergeRepo(ctx context.Context, params *UserSettingsLoadAndMergeRepoRequest) (*UserSettingsLoadAndMergeRepoResult, error) { + raw, err := a.client.Request(ctx, "user.settings.loadAndMergeRepo", params) + if err != nil { + return nil, err + } + var result UserSettingsLoadAndMergeRepoResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// LoadForConfigDir reads the user's settings document exactly as `user.settings.load` does, +// but from a caller-supplied configuration directory instead of the one the server resolved +// for itself. Use this when a consumer scopes a session to its own Copilot home — the SDK's +// per-session `configDir` override — so the settings read match the directory that session +// actually uses. An absent or empty `configDir` resolves the server's own home, making this +// identical to `user.settings.load`. Hook-validation relaxations are deliberately not +// caller-supplied: they continue to come from the server's environment, so a consumer +// cannot widen the host's hook policy through this method. +// +// RPC method: user.settings.loadForConfigDir. +// +// Parameters: Selects the configuration directory whose user settings to read. +// +// Returns: The stored user settings document, keyed by setting name. +// Internal: LoadForConfigDir is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerUserSettingsAPI) LoadForConfigDir(ctx context.Context, params *UserSettingsLoadForConfigDirRequest) (*UserSettingsLoadForConfigDirResult, error) { + raw, err := a.client.Request(ctx, "user.settings.loadForConfigDir", params) + if err != nil { + return nil, err + } + var result UserSettingsLoadForConfigDirResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// LoadForEdit reads settings.json as stored for a caller that is about to edit it. Unlike +// `user.settings.get`, this returns file values rather than per-key metadata and defaults. +// Reports `missing` for an absent file and `invalid` when the existing file will not parse. +// Retired config.json and extensionless files are not read. +// +// RPC method: user.settings.loadForEdit. +// +// Returns: Contents of settings.json as stored, for a caller about to rewrite it. +// Internal: LoadForEdit is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerUserSettingsAPI) LoadForEdit(ctx context.Context) (*UserSettingsLoadForEditResult, error) { + raw, err := a.client.Request(ctx, "user.settings.loadForEdit", nil) + if err != nil { + return nil, err + } + var result UserSettingsLoadForEditResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// LoadRepo reads a repository's checked-in settings document as stored, normalized, +// together with any diagnostic raised while reading it. This is the repository half of the +// layering `user.settings.get` reports: `user.settings.load` reads the user's own document, +// and this reads the one a repository checks in to govern the keys +// `user.settings.governanceKeys` lists as repo-governable. A document that fails to parse, +// or that sets a key a repository may not govern, is reported through `warning` while the +// settings that did load are still returned, so a consumer surfaces the diagnostic and +// continues rather than failing the session. When `repoRoot` is the user's home directory +// the Claude-compatible configuration is skipped, so a home-directory session does not +// inherit a personal config as repository policy. +// +// RPC method: user.settings.loadRepo. +// +// Parameters: Selects the repository whose checked-in settings to read. +// +// Returns: A repository's settings as stored, together with any diagnostic raised while +// reading them. +// Internal: LoadRepo is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerUserSettingsAPI) LoadRepo(ctx context.Context, params *UserSettingsLoadRepoRequest) (*UserSettingsLoadRepoResult, error) { + raw, err := a.client.Request(ctx, "user.settings.loadRepo", params) + if err != nil { + return nil, err + } + var result UserSettingsLoadRepoResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// LoadWithWarning reads the user's stored settings together with the diagnostic raised +// while reading them. This is `user.settings.load` plus the reason part of the document was +// ignored: that one answers the document alone, so a caller cannot tell a clean read from a +// salvaged one. Read this when the caller is about to write the file back, where accepting +// a partial parse would discard every value the loader could not read. The settings that +// did load are still reported alongside the warning, so a consumer may surface it and +// continue instead of failing. +// +// RPC method: user.settings.loadWithWarning. +// +// Returns: The user's stored settings, together with any diagnostic raised while reading +// them. +// Internal: LoadWithWarning is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerUserSettingsAPI) LoadWithWarning(ctx context.Context) (*UserSettingsLoadWithWarningResult, error) { + raw, err := a.client.Request(ctx, "user.settings.loadWithWarning", nil) + if err != nil { + return nil, err + } + var result UserSettingsLoadWithWarningResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// MergeManagedAndUser folds a user settings layer over a managed (enterprise policy) layer +// and returns the merged settings. The managed layer's governance keys win, `autoTier` +// resolves through its overridable form, and the two per-entry control maps +// (`enabledPlugins`, `extraKnownMarketplaces`) union across the layers and are then stamped +// back from the managed layer, so each entry an administrator declares wins while the user +// keeps control of the rest. The parameters are named for their layers rather than being a +// generic base/override pair: nothing about a settings object identifies which layer +// produced it, so the runtime cannot detect a transposed call, and transposing one resolves +// the governance keys in the user's favour instead of the administrator's. Supplying only +// one layer returns it unchanged, and supplying neither returns an empty object. Rejects a +// managed `autoTier` that is neither a user-configurable tier nor a single-key overridable +// form, rather than silently dropping it. +// +// RPC method: user.settings.mergeManagedAndUser. +// +// Parameters: The managed (enterprise policy) layer and the user layer to fold together. +// The field names carry the layer identity: nothing about a settings object identifies +// which layer produced it, so a generic base/override pair would let a caller transpose +// them and silently resolve the governance keys in the user's favour. +// +// Returns: The user layer folded over the managed layer. +// Internal: MergeManagedAndUser is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerUserSettingsAPI) MergeManagedAndUser(ctx context.Context, params *UserSettingsMergeManagedAndUserParams) (*UserSettingsMergeManagedAndUserResult, error) { + raw, err := a.client.Request(ctx, "user.settings.mergeManagedAndUser", params) + if err != nil { + return nil, err + } + var result UserSettingsMergeManagedAndUserResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// MergeRepoValue folds a repository's settings layer into the user layer. Each +// repo-overridable key carries a directional policy chosen so a repository can only add +// restrictions and never relax the user's own. The two per-entry managed control maps are +// the exception those directional rules cannot express — a repository may legitimately +// enable plugins and declare marketplaces — so they are re-stamped afterwards from the +// runtime's ambient managed policy. That makes the result depend on the runtime process's +// managed state and not on the arguments alone: a host must call this rather than +// reimplement the fold, or a repository could relax a restriction the administrator +// imposed. Rejects when either layer is not a JSON object. +// +// RPC method: user.settings.mergeRepoValue. +// +// Parameters: The user layer and the repository layer to fold together. +// +// Returns: The repository layer folded into the user layer. +// Internal: MergeRepoValue is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerUserSettingsAPI) MergeRepoValue(ctx context.Context, params *UserSettingsMergeRepoValueParams) (*UserSettingsMergeRepoValueResult, error) { + raw, err := a.client.Request(ctx, "user.settings.mergeRepoValue", params) + if err != nil { + return nil, err + } + var result UserSettingsMergeRepoValueResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// MigrateObsoleteOnDisk runs the one-time cleanup that removes obsolete CLI-owned settings +// and legacy rubber-duck keys from the user's settings document, and reports what it did. +// Idempotent and safe to call on every startup: with nothing obsolete left it reports +// `unchanged` and writes nothing. The cleanup preserves every key it does not recognize, so +// a setting written by a newer host is never dropped by an older one, and a document that +// fails to parse is reported `invalid` and left untouched rather than rewritten from a +// partial read. The cleaned document is not returned: it is written before this replies, so +// `user.settings.load` reads it authoritatively. A host that surfaces unrecognized keys +// through `user.settings.unknownKeys` wants this first, because the keys this retires would +// otherwise be shown to the user as if they were misspelled settings. +// +// RPC method: user.settings.migrateObsoleteOnDisk. +// +// Parameters: Selects the configuration directory whose settings document to clean. +// +// Returns: The outcome of the one-time obsolete-settings cleanup. +// Internal: MigrateObsoleteOnDisk is part of the SDK's internal handshake/plumbing; +// external callers should not use it. +func (a *InternalServerUserSettingsAPI) MigrateObsoleteOnDisk(ctx context.Context, params *UserSettingsMigrateObsoleteOnDiskRequest) (*UserSettingsMigrateObsoleteOnDiskResult, error) { + raw, err := a.client.Request(ctx, "user.settings.migrateObsoleteOnDisk", params) + if err != nil { + return nil, err + } + var result UserSettingsMigrateObsoleteOnDiskResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// MigrateStaffLogLevelOnce runs the one-time migration that moves a staff account's log +// level to the current default, and records that it ran. Idempotent and self-gating: it +// takes a lock over the global-state file and re-reads the completion stamp inside it, so +// two hosts racing at startup cannot both migrate. Only `refreshed` can have changed the +// persisted settings, and the settings it returns are the document re-read after the write, +// so a host should adopt them rather than reloading separately and racing the next writer. +// +// RPC method: user.settings.migrateStaffLogLevelOnce. +// +// Parameters: Selects the configuration directory to run the one-time staff log-level +// migration against, and supplies the caller's record of whether it has already run. +// +// Returns: The outcome of the one-time staff log-level migration. +// Internal: MigrateStaffLogLevelOnce is part of the SDK's internal handshake/plumbing; +// external callers should not use it. +func (a *InternalServerUserSettingsAPI) MigrateStaffLogLevelOnce(ctx context.Context, params *UserSettingsMigrateStaffLogLevelOnceRequest) (*UserSettingsMigrateStaffLogLevelOnceResult, error) { + raw, err := a.client.Request(ctx, "user.settings.migrateStaffLogLevelOnce", params) + if err != nil { + return nil, err + } + var result UserSettingsMigrateStaffLogLevelOnceResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// MigrateStaffUpdateChannelOnce runs the one-time migration that moves a staff account's +// update channel to stable, and records that it ran. Idempotent and self-gating: it takes a +// lock over the global-state file, re-reads the completion stamp inside it, and returns +// `current` rather than migrating twice, so two hosts racing at startup cannot both +// migrate. Only a `migrated` status means the persisted settings changed and the caller +// should reload them. An account on the experimental channel is deliberately left alone and +// reported as `excluded-experimental`. +// +// RPC method: user.settings.migrateStaffUpdateChannelOnce. +// +// Parameters: The filesystem and environment facts a user-settings file is resolved from. +// Passed explicitly rather than read from the runtime's own process, so a host whose +// configuration directory differs from its process environment resolves the file it means. +// +// Returns: The outcome of the one-time staff update-channel migration. +// Internal: MigrateStaffUpdateChannelOnce is part of the SDK's internal handshake/plumbing; +// external callers should not use it. +func (a *InternalServerUserSettingsAPI) MigrateStaffUpdateChannelOnce(ctx context.Context, params *UserSettingsHostContext) (*UserSettingsMigrateStaffUpdateChannelOnceResult, error) { + raw, err := a.client.Request(ctx, "user.settings.migrateStaffUpdateChannelOnce", params) + if err != nil { + return nil, err + } + var result UserSettingsMigrateStaffUpdateChannelOnceResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Normalize rewrites legacy and snake_case setting names in a settings object to their +// canonical spelling. A caller editing settings.json must normalize before writing: a +// legacy key left in place survives the write, is re-normalized on the next load, and +// silently shadows the canonical value the write just stored. +// +// RPC method: user.settings.normalize. +// +// Parameters: Settings object whose key names should be canonicalized. +// +// Returns: Settings object with canonical key names. +// Internal: Normalize is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerUserSettingsAPI) Normalize(ctx context.Context, params *UserSettingsNormalizeRequest) (*UserSettingsNormalizeResult, error) { + raw, err := a.client.Request(ctx, "user.settings.normalize", params) + if err != nil { + return nil, err + } + var result UserSettingsNormalizeResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// ResetStaffModelSelectionOnce runs the one-time reset that clears a staff account's pinned +// model selection, and reports the timestamp the host should record. Idempotent and +// self-gating: the reset is gated on a fixed cutoff, so a caller that passes back the +// timestamp it recorded gets `didReset: false` rather than a second reset. It applies to +// staff accounts only; any other account is reported as not reset without the settings +// document being opened. Only `didReset: true` means the persisted settings changed, and +// only then are the updated settings returned for the host to adopt. +// +// RPC method: user.settings.resetStaffModelSelectionOnce. +// +// Parameters: Selects the configuration directory to run the one-time staff model-selection +// reset against, and supplies the caller's record of whether it has already run. +// +// Returns: The outcome of the one-time staff model-selection reset. +// Internal: ResetStaffModelSelectionOnce is part of the SDK's internal handshake/plumbing; +// external callers should not use it. +func (a *InternalServerUserSettingsAPI) ResetStaffModelSelectionOnce(ctx context.Context, params *UserSettingsResetStaffModelSelectionOnceRequest) (*UserSettingsResetStaffModelSelectionOnceResult, error) { + raw, err := a.client.Request(ctx, "user.settings.resetStaffModelSelectionOnce", params) + if err != nil { + return nil, err + } + var result UserSettingsResetStaffModelSelectionOnceResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// SetHookDisabled adds or removes one hook key in the user `disabledHooks` setting, +// rewriting settings.json in place and leaving every other setting untouched. Reports a +// typed outcome rather than failing the call, so a host can tell an unparseable settings +// file apart from a write that could not complete. +// +// RPC method: user.settings.setHookDisabled. +// +// Parameters: One hook to disable or re-enable. +// +// Returns: Outcome of rewriting the `disabledHooks` setting. +// Internal: SetHookDisabled is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerUserSettingsAPI) SetHookDisabled(ctx context.Context, params *UserSettingsSetHookDisabledRequest) (*UserSettingsSetHookDisabledResult, error) { + raw, err := a.client.Request(ctx, "user.settings.setHookDisabled", params) + if err != nil { + return nil, err + } + var result UserSettingsSetHookDisabledResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Update merges a partial object into one top-level user setting, re-reading the stored +// value inside this runtime process's settings.json write lock so that concurrent writers +// in this process to different sub-fields of the same key do not clobber each other. Unlike +// `user.settings.set`, which replaces a key's whole value with what the caller last read, +// this keeps sub-fields the caller did not name when both the stored value and supplied +// value are objects; otherwise the supplied value replaces the stored value. Use it +// whenever two independent surfaces own different sub-fields of one setting. Returns the +// key's stored value after the write, in `value`. +// +// RPC method: user.settings.update. +// +// Parameters: Names the user setting to merge into, and the partial object to merge. +// +// Returns: The stored value of the user setting after the merge. +// Internal: Update is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerUserSettingsAPI) Update(ctx context.Context, params *UserSettingsUpdateRequest) (*UserSettingsUpdateResult, error) { + raw, err := a.client.Request(ctx, "user.settings.update", params) + if err != nil { + return nil, err + } + var result UserSettingsUpdateResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Validates one top-level setting subtree without writing it, reporting each problem with +// the path it applies to. Scoped to a single top-level key on purpose: validating the whole +// file would reject an edit whenever an unrelated pre-existing field is malformed, locking +// the user out of the very tool that could fix it. +// +// RPC method: user.settings.validate. +// +// Parameters: A single top-level setting subtree to validate. +// +// Returns: Outcome of validating one top-level setting subtree. +// Internal: Validate is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerUserSettingsAPI) Validate(ctx context.Context, params *UserSettingsValidateRequest) (*UserSettingsValidateResult, error) { + raw, err := a.client.Request(ctx, "user.settings.validate", params) + if err != nil { + return nil, err + } + var result UserSettingsValidateResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// WriteAll replaces the user settings file's entire content in one write. A key absent from +// `settings` is removed, which is what distinguishes this from `user.settings.set`: that +// one merges, so it can never clear a key the caller has dropped. For a caller saving the +// result of a raw edit, a merge would silently resurrect every key the user deleted. +// Records the same one-time mutation marks a per-key write records, so a setting the user +// has now chosen explicitly stops being migrated for them. +// +// RPC method: user.settings.writeAll. +// +// Parameters: The whole content to store in the user settings file. +// Internal: WriteAll is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerUserSettingsAPI) WriteAll(ctx context.Context, params *UserSettingsWriteAllRequest) (*UserSettingsWriteAllResult, error) { + raw, err := a.client.Request(ctx, "user.settings.writeAll", params) + if err != nil { + return nil, err + } + var result UserSettingsWriteAllResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: Settings returns experimental APIs that may change or be removed. +func (s *InternalServerUserAPI) Settings() *InternalServerUserSettingsAPI { + return (*InternalServerUserSettingsAPI)(s) +} + // InternalServerRPC provides internal SDK server-scoped RPC methods (handshake helpers // etc.). Not part of the public API. type InternalServerRPC struct { // Reuse a single struct instead of allocating one for each service on the heap. common internalServerAPI + Account *InternalServerAccountAPI Agents *InternalServerAgentsAPI + FeatureFlags *InternalServerFeatureFlagsAPI Git *InternalServerGitAPI GitHubOwners *InternalServerGitHubOwnersAPI GitHubRepository *InternalServerGitHubRepositoryAPI GlobalState *InternalServerGlobalStateAPI Host *InternalServerHostAPI + Instructions *InternalServerInstructionsAPI + ManagedSettings *InternalServerManagedSettingsAPI + MCP *InternalServerMCPAPI + Permissions *InternalServerPermissionsAPI + Persistence *InternalServerPersistenceAPI + Repo *InternalServerRepoAPI + Sandbox *InternalServerSandboxAPI Sessions *InternalServerSessionsAPI + User *InternalServerUserAPI } // Connect performs the SDK server connection handshake and validates the optional @@ -31449,13 +36297,23 @@ func (a *InternalServerRPC) Connect(ctx context.Context, params *ConnectRequest) func NewInternalServerRPC(client *jsonrpc2.Client) *InternalServerRPC { r := &InternalServerRPC{} r.common = internalServerAPI{client: client} + r.Account = (*InternalServerAccountAPI)(&r.common) r.Agents = (*InternalServerAgentsAPI)(&r.common) + r.FeatureFlags = (*InternalServerFeatureFlagsAPI)(&r.common) r.Git = (*InternalServerGitAPI)(&r.common) r.GitHubOwners = (*InternalServerGitHubOwnersAPI)(&r.common) r.GitHubRepository = (*InternalServerGitHubRepositoryAPI)(&r.common) r.GlobalState = (*InternalServerGlobalStateAPI)(&r.common) r.Host = (*InternalServerHostAPI)(&r.common) + r.Instructions = (*InternalServerInstructionsAPI)(&r.common) + r.ManagedSettings = (*InternalServerManagedSettingsAPI)(&r.common) + r.MCP = (*InternalServerMCPAPI)(&r.common) + r.Permissions = (*InternalServerPermissionsAPI)(&r.common) + r.Persistence = (*InternalServerPersistenceAPI)(&r.common) + r.Repo = (*InternalServerRepoAPI)(&r.common) + r.Sandbox = (*InternalServerSandboxAPI)(&r.common) r.Sessions = (*InternalServerSessionsAPI)(&r.common) + r.User = (*InternalServerUserAPI)(&r.common) return r } @@ -34621,6 +39479,43 @@ func (a *ModelAPI) GetCurrent(ctx context.Context) (*CurrentModel, error) { return &result, nil } +// GetReasoningSummaries computes whether the session's current model shows reasoning +// summaries and how a host should present them: the default Ctrl+T visibility, reasoning +// headers, the hidden-summaries hint, and whether a persisted `showReasoning` setting is +// redundant. The runtime folds in the session's model catalog, selection and +// reasoning-summaries experiment assignment; the host supplies only its own persisted +// setting and current visibility. This method reads the session's resolved model catalog +// rather than resolving it, so call `session.model.list` first on a session that has not +// listed models or run a turn; until then the result is `ready: false`. A session that +// resolves models without a Copilot catalog, that is a BYOK registry or an alternate +// provider, never caches one and stays `ready: false`. +// +// RPC method: session.model.getReasoningSummaries. +// +// Parameters: Host presentation state the reasoning-summaries policy depends on. +// +// Returns: Reasoning-summaries presentation policy for the session's current model. +func (a *ModelAPI) GetReasoningSummaries(ctx context.Context, params *ModelGetReasoningSummariesRequest) (*ModelGetReasoningSummariesResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + if params.PersistedShowReasoning != nil { + req["persistedShowReasoning"] = *params.PersistedShowReasoning + } + if params.SummariesVisible != nil { + req["summariesVisible"] = *params.SummariesVisible + } + } + raw, err := a.client.Request(ctx, "session.model.getReasoningSummaries", req) + if err != nil { + return nil, err + } + var result ModelGetReasoningSummariesResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // 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. @@ -34900,6 +39795,9 @@ func (a *OptionsAPI) Update(ctx context.Context, params *SessionUpdateOptionsPar if params.AgentContext != nil { req["agentContext"] = *params.AgentContext } + if params.AgentDirectories != nil { + req["agentDirectories"] = params.AgentDirectories + } if params.AllowAllMCPServerInstructions != nil { req["allowAllMcpServerInstructions"] = *params.AllowAllMCPServerInstructions } @@ -34978,6 +39876,9 @@ func (a *OptionsAPI) Update(ctx context.Context, params *SessionUpdateOptionsPar if params.FeatureFlags != nil { req["featureFlags"] = params.FeatureFlags } + if params.HookDirectories != nil { + req["hookDirectories"] = params.HookDirectories + } if params.IgnoredSkillsLocations != nil { req["ignoredSkillsLocations"] = params.IgnoredSkillsLocations } @@ -34990,6 +39891,9 @@ func (a *OptionsAPI) Update(ctx context.Context, params *SessionUpdateOptionsPar if params.InstalledPlugins != nil { req["installedPlugins"] = params.InstalledPlugins } + if params.InstructionDirectories != nil { + req["instructionDirectories"] = params.InstructionDirectories + } if params.IntegrationID != nil { req["integrationId"] = *params.IntegrationID } @@ -35891,6 +40795,40 @@ func (a *PluginsAPI) Reload(ctx context.Context, params ...*SessionPluginsReload return &result, nil } +// RetryManaged installs or updates the plugins the session's managed settings require now, +// without waiting for the next automatic attempt after a failure, then reloads plugins. +// Reports progress and failures with the same `session.info` and `session.warning` events +// as automatic preparation. Fails when the runtime does not prepare managed plugins for the +// session. +// +// RPC method: session.plugins.retryManaged. +// +// Parameters: Plugins required by the session's managed settings to install or update again. +// +// Returns: Per-plugin outcomes of retrying the plugins required by the session's managed +// settings. +func (a *PluginsAPI) RetryManaged(ctx context.Context, params ...*SessionPluginsRetryManagedRequest) (*SessionPluginsRetryManagedResult, error) { + var requestParams *SessionPluginsRetryManagedRequest + if len(params) > 0 { + requestParams = params[0] + } + req := map[string]any{"sessionId": a.sessionID} + if requestParams != nil { + if requestParams.Plugins != nil { + req["plugins"] = requestParams.Plugins + } + } + raw, err := a.client.Request(ctx, "session.plugins.retryManaged", req) + if err != nil { + return nil, err + } + var result SessionPluginsRetryManagedResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // Uninstalls a plugin when permitted by the live session's retained managed policy. // // RPC method: session.plugins.uninstall. @@ -36800,6 +41738,29 @@ func (a *SandboxAPI) DisableForSession(ctx context.Context, params *SandboxDisab return &result, nil } +// GetCredentialSuggestions lists possible secret-bearing environment variables available to +// new sandboxed shells that are not already covered by credential masking. Uses local name +// rules and known-provider host suggestions, never returns secret values, and changes no +// settings or network permissions. Returns no suggestions while the session is not +// sandboxed. +// +// RPC method: session.sandbox.getCredentialSuggestions. +// +// Returns: Possible unconfigured secrets in the sandbox shell environment, sorted by +// variable name, with no secret values. +func (a *SandboxAPI) GetCredentialSuggestions(ctx context.Context) (*SandboxCredentialSuggestionsResult, error) { + req := map[string]any{"sessionId": a.sessionID} + raw, err := a.client.Request(ctx, "session.sandbox.getCredentialSuggestions", req) + if err != nil { + return nil, err + } + var result SandboxCredentialSuggestionsResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // GetEnforcementStatus returns whether managed policy requires sandbox enforcement and // whether an enforcement failure has permanently blocked the session. // diff --git a/go/rpc/zrpc_encoding.go b/go/rpc/zrpc_encoding.go index 471e491a87..fdddf41f89 100644 --- a/go/rpc/zrpc_encoding.go +++ b/go/rpc/zrpc_encoding.go @@ -7721,6 +7721,7 @@ func (r *SessionOpenOptions) UnmarshalJSON(data []byte) error { AdditionalContentExclusionPolicies []SessionOpenOptionsAdditionalContentExclusionPolicy `json:"additionalContentExclusionPolicies,omitzero"` AdditionalDirectories []string `json:"additionalDirectories,omitzero"` AgentContext *string `json:"agentContext,omitempty"` + AgentDirectories []string `json:"agentDirectories,omitzero"` AllowAllMCPServerInstructions *bool `json:"allowAllMcpServerInstructions,omitempty"` AskUserDisabled *bool `json:"askUserDisabled,omitempty"` AuthClientIDMetadataURL *string `json:"authClientIdMetadataUrl,omitempty"` @@ -7756,10 +7757,12 @@ func (r *SessionOpenOptions) UnmarshalJSON(data []byte) error { ExpAssignments any `json:"expAssignments,omitempty"` FeatureFlags map[string]bool `json:"featureFlags,omitzero"` HasSkillProvider *bool `json:"hasSkillProvider,omitempty"` + HookDirectories []string `json:"hookDirectories,omitzero"` IgnoredSkillsLocations []string `json:"ignoredSkillsLocations,omitzero"` IncludedBuiltinAgents []string `json:"includedBuiltinAgents,omitzero"` IncludedBuiltinSkills []string `json:"includedBuiltinSkills,omitzero"` InstalledPlugins []InstalledPlugin `json:"installedPlugins,omitzero"` + InstructionDirectories []string `json:"instructionDirectories,omitzero"` IntegrationID *string `json:"integrationId,omitempty"` IsExperimentalMode *bool `json:"isExperimentalMode,omitempty"` LogInteractiveShells *bool `json:"logInteractiveShells,omitempty"` @@ -7803,6 +7806,7 @@ func (r *SessionOpenOptions) UnmarshalJSON(data []byte) error { r.AdditionalContentExclusionPolicies = raw.AdditionalContentExclusionPolicies r.AdditionalDirectories = raw.AdditionalDirectories r.AgentContext = raw.AgentContext + r.AgentDirectories = raw.AgentDirectories r.AllowAllMCPServerInstructions = raw.AllowAllMCPServerInstructions r.AskUserDisabled = raw.AskUserDisabled r.AuthClientIDMetadataURL = raw.AuthClientIDMetadataURL @@ -7844,10 +7848,12 @@ func (r *SessionOpenOptions) UnmarshalJSON(data []byte) error { r.ExpAssignments = raw.ExpAssignments r.FeatureFlags = raw.FeatureFlags r.HasSkillProvider = raw.HasSkillProvider + r.HookDirectories = raw.HookDirectories r.IgnoredSkillsLocations = raw.IgnoredSkillsLocations r.IncludedBuiltinAgents = raw.IncludedBuiltinAgents r.IncludedBuiltinSkills = raw.IncludedBuiltinSkills r.InstalledPlugins = raw.InstalledPlugins + r.InstructionDirectories = raw.InstructionDirectories r.IntegrationID = raw.IntegrationID r.IsExperimentalMode = raw.IsExperimentalMode r.LogInteractiveShells = raw.LogInteractiveShells diff --git a/go/rpc/zsession_encoding.go b/go/rpc/zsession_encoding.go index 2db5b44582..89810fdf9b 100644 --- a/go/rpc/zsession_encoding.go +++ b/go/rpc/zsession_encoding.go @@ -2363,6 +2363,8 @@ func (r PermissionPromptRequestWrite) MarshalJSON() ([]byte, error) { func (r *PermissionRequestedData) UnmarshalJSON(data []byte) error { type rawPermissionRequestedData struct { AgentMode *SessionMode `json:"agentMode,omitempty"` + OwnerAgentID *string `json:"ownerAgentId,omitempty"` + OwnerSessionID *string `json:"ownerSessionId,omitempty"` PermissionMode *PermissionMode `json:"permissionMode,omitempty"` PermissionRequest json.RawMessage `json:"permissionRequest"` PromptRequest json.RawMessage `json:"promptRequest,omitempty"` @@ -2376,6 +2378,8 @@ func (r *PermissionRequestedData) UnmarshalJSON(data []byte) error { return err } r.AgentMode = raw.AgentMode + r.OwnerAgentID = raw.OwnerAgentID + r.OwnerSessionID = raw.OwnerSessionID r.PermissionMode = raw.PermissionMode if raw.PermissionRequest != nil { value, err := unmarshalPermissionRequest(raw.PermissionRequest) diff --git a/go/rpc/zsession_events.go b/go/rpc/zsession_events.go index a9bc3e7a63..cde63686ae 100644 --- a/go/rpc/zsession_events.go +++ b/go/rpc/zsession_events.go @@ -358,7 +358,7 @@ func (*PromptCacheBreakData) Type() SessionEventType { return SessionEventTypePr // A provider-owned quota observation, distinct from per-call usage and charge accounting. // Experimental: SessionQuotaObservationData is part of an experimental API and may change or be removed. type SessionQuotaObservationData struct { - // The admitted provider's state observation. Admission observations never contain quantities or reset/percentage semantics. + // The admitted provider's state observation. Admission balances and percentages remain absent; separate monthly usage may report its own amount and cycle. Observation ProviderQuotaState `json:"observation"` } @@ -487,6 +487,10 @@ type SessionPermissionRecoveryData struct { MaxAttempts int64 `json:"maxAttempts"` // Policy selected from the current client's response capability; mode or client changes may update it during recovery OnBlocked PermissionRecoveryOnBlocked `json:"onBlocked"` + // Subagent identifier that owns this recovery episode when the request was delegated through a parent permission service + OwnerAgentID *string `json:"ownerAgentId,omitempty"` + // Root or child execution-session identifier that owns this recovery episode; omitted for legacy snapshots + OwnerSessionID *string `json:"ownerSessionId,omitempty"` // Controlled reason for the latest episode transition Reason PermissionRecoveryReason `json:"reason"` // Current lifecycle state of the recovery episode @@ -895,18 +899,20 @@ func (*MCPHeadersRefreshRequiredData) Type() SessionEventType { // Effective enterprise managed settings and contributing channels. Session events report applied policy; sessionless resolve reports an account/device snapshot, and compose reports a non-applying preview of candidate documents. Device values take precedence over server values, then the policy helper, per ordinary key, while permissions compose restrictively. Session-local SDK-client policy is included only in session results. Marked experimental while the managed-settings surface stabilizes. // Experimental: SessionManagedSettingsResolvedData is part of an experimental API and may change or be removed. type SessionManagedSettingsResolvedData struct { - // Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers, and forced on when `failClosed` is true. + // Whether an explicit enterprise policy restriction disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers; an unresolved policy does not force this on by itself. BypassPermissionsDisabled bool `json:"bypassPermissionsDisabled"` // Whether a session-local permissions layer injected by the SDK host was present ClientManaged *bool `json:"clientManaged,omitempty"` // Whether an actual device MDM/plist/registry/file managed-settings layer was present DeviceManaged bool `json:"deviceManaged"` - // Whether managed policy could not be determined (e.g. a failed server fetch) and the session fell back to the fail-closed restriction. When true, restrictions such as disabling bypass-permissions are enforced even though `settings` may be absent. + // Whether managed policy could not be determined (e.g. a failed server fetch) and unresolved-policy safeguards remain active. This does not by itself disable bypass-permissions; `bypassPermissionsDisabled` reports only an explicit policy restriction. FailClosed bool `json:"failClosed"` // The setting keys under enterprise management in the effective managed settings (e.g. `model`, `enabledPlugins`, `permissions`). Empty when no managed settings are in force. ManagedKeys []string `json:"managedKeys"` // Whether at least two managed sources supplied permission allowlists, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. PermissionsAllowIntersected *bool `json:"permissionsAllowIntersected,omitempty"` + // Reusable retained managed permission policy for managedSettings.permissions.evaluate, including source composition and session-local injection. Provided by live session snapshots; absent in older events. Refresh the context when managed policy changes. + PermissionsContext *ManagedPermissionsContext `json:"permissionsContext,omitempty"` // Whether the policy-helper managed-settings layer was present. The policy helper is the weakest channel: it fills keys no enterprise source set and can never replace one. PolicyHelperManaged *bool `json:"policyHelperManaged,omitempty"` // Whether the effective sandbox policy forces the sandbox on *only* because managed policy could not be determined, rather than because the policy requires it. Lets clients tell a user whose `--no-sandbox` was overridden that the sandbox stayed on as a fail-closed fallback, instead of attributing it to an administrator who set no such policy. @@ -1046,12 +1052,16 @@ type SessionErrorData struct { EligibleForAutoSwitch *bool `json:"eligibleForAutoSwitch,omitempty"` // Fine-grained error code from the upstream provider, when available. For `errorType: "rate_limit"`, this is one of the `RateLimitErrorCode` values (e.g., `"user_weekly_rate_limited"`, `"user_global_rate_limited"`, `"rate_limited"`, `"user_model_rate_limited"`, `"integration_rate_limited"`). For `errorType: "quota"`, this is the CAPI quota error code (e.g., `"quota_exceeded"`, `"session_quota_exceeded"`, `"billing_not_configured"`). ErrorCode *string `json:"errorCode,omitempty"` - // Category of error (e.g., "authentication", "authorization", "quota", "rate_limit", "context_limit", "query") + // Category of error (e.g., "authentication", "authorization", "policy", "quota", "rate_limit", "context_limit", "query") ErrorType string `json:"errorType"` // Human-readable error message Message string `json:"message"` // GitHub request tracing ID (x-github-request-id header) for correlating with server-side logs ProviderCallID *string `json:"providerCallId,omitempty"` + // Sanitized provider-defined error parameter preserved from a structured upstream error envelope, when available. + ProviderErrorParam *string `json:"providerErrorParam,omitempty"` + // Provider-defined error type preserved from a structured upstream error envelope, when available (for example, `invalid_request_error`). + ProviderErrorType *string `json:"providerErrorType,omitempty"` // What the user must do to recover, when the runtime knows of an action. The `message` never names a client affordance, so a client that offers one — a slash command, a settings pane, a link — renders it from this value. Remediation *RemediationAction `json:"remediation,omitempty"` // Copilot service request ID (x-copilot-service-request-id header) for CAPI log correlation @@ -1471,8 +1481,12 @@ type ModelCallFailureData struct { QuotaSnapshots map[string]AssistantUsageQuotaSnapshot `json:"quotaSnapshots,omitzero"` // Reasoning effort level used for the failed model call, if applicable ReasoningEffort *string `json:"reasoningEffort,omitempty"` - // Serialized (uncompressed) byte length of the failed request body. A content-free size signal. + // Serialized (uncompressed) byte length of the failed request body, before any Content-Encoding. A content-free size signal. RequestBodyBytes *int64 `json:"requestBodyBytes,omitempty"` + // Content-Encoding applied to the failed request body; identity when uncompressed + RequestBodyEncoding *ModelCallRequestBodyEncoding `json:"requestBodyEncoding,omitempty"` + // Byte length of the failed request body actually sent on the wire, after any Content-Encoding. Equals requestBodyBytes when the body was not compressed. + RequestBodyWireBytes *int64 `json:"requestBodyWireBytes,omitempty"` // Content-free structural summary of the failing request. Contains only counts and shape flags (no prompt content), so it is safe for unrestricted telemetry. Populated only for client-error (4xx) failures. RequestFingerprint *ModelCallFailureRequestFingerprint `json:"requestFingerprint,omitempty"` // Zero-based orchestrator retry index of the failed attempt @@ -1489,8 +1503,18 @@ type ModelCallFailureData struct { Transport *ModelCallFailureTransport `json:"transport,omitempty"` // Milliseconds spent on the WebSocket attempt before falling back to HTTP WebsocketFallbackAfterMs *int64 `json:"websocketFallbackAfterMs,omitempty"` + // WebSocket close code received before the HTTP fallback + WebsocketFallbackCloseCode *int32 `json:"websocketFallbackCloseCode,omitempty"` + // Normalized cause of the WebSocket failure that triggered the HTTP fallback; absent when no fallback occurred or the cause is unknown + WebsocketFallbackErrorKind *ModelCallWebSocketFallbackErrorKind `json:"websocketFallbackErrorKind,omitempty"` // Why the failed call was carried by the HTTP fallback of a WebSocket-capable dispatcher; absent when no fallback occurred WebsocketFallbackReason *ModelCallWebSocketFallbackReason `json:"websocketFallbackReason,omitempty"` + // Whether a WebSocket reconnect was attempted before falling back to HTTP + WebsocketFallbackReconnectAttempted *bool `json:"websocketFallbackReconnectAttempted,omitempty"` + // Whether the HTTP fallback started during this model call, rather than being inherited from an earlier call + WebsocketFallbackStartedThisCall *bool `json:"websocketFallbackStartedThisCall,omitempty"` + // HTTP status of the WebSocket upgrade rejection or WebSocket API error that triggered the HTTP fallback + WebsocketFallbackStatusCode *int32 `json:"websocketFallbackStatusCode,omitempty"` } func (*ModelCallFailureData) sessionEventData() {} @@ -1854,8 +1878,12 @@ type AssistantUsageData struct { ReasoningTokens *int64 `json:"reasoningTokens,omitempty"` // Number of rejected speculative prediction tokens RejectedPredictionTokens *int64 `json:"rejectedPredictionTokens,omitempty"` - // Serialized (uncompressed) byte length of the request body. A content-free size signal. + // Serialized (uncompressed) byte length of the request body, before any Content-Encoding. A content-free size signal. RequestBodyBytes *int64 `json:"requestBodyBytes,omitempty"` + // Content-Encoding applied to the request body of the attempt that produced this outcome; identity when uncompressed + RequestBodyEncoding *ModelCallRequestBodyEncoding `json:"requestBodyEncoding,omitempty"` + // Byte length of the request body actually sent on the wire for the attempt that produced this outcome, after any Content-Encoding. Equals requestBodyBytes when the body was not compressed. + RequestBodyWireBytes *int64 `json:"requestBodyWireBytes,omitempty"` // Per-request treatment/eligibility signal returned by the Copilot API in the `X-GitHub-Copilot-Request-TE` response header for the associated model call; `false` when the header was absent or unparseable. Rte *bool `json:"rte,omitempty"` // Copilot service request ID (x-copilot-service-request-id header) for CAPI log correlation @@ -1878,8 +1906,18 @@ type AssistantUsageData struct { Transport *AssistantUsageTransport `json:"transport,omitempty"` // Milliseconds spent on the WebSocket attempt before falling back to HTTP WebsocketFallbackAfterMs *int64 `json:"websocketFallbackAfterMs,omitempty"` + // WebSocket close code received before the HTTP fallback + WebsocketFallbackCloseCode *int32 `json:"websocketFallbackCloseCode,omitempty"` + // Normalized cause of the WebSocket failure that triggered the HTTP fallback; absent when no fallback occurred or the cause is unknown + WebsocketFallbackErrorKind *ModelCallWebSocketFallbackErrorKind `json:"websocketFallbackErrorKind,omitempty"` // Why the call was carried by the HTTP fallback of a WebSocket-capable dispatcher; absent when no fallback occurred WebsocketFallbackReason *ModelCallWebSocketFallbackReason `json:"websocketFallbackReason,omitempty"` + // Whether a WebSocket reconnect was attempted before falling back to HTTP + WebsocketFallbackReconnectAttempted *bool `json:"websocketFallbackReconnectAttempted,omitempty"` + // Whether the HTTP fallback started during this model call, rather than being inherited from an earlier call + WebsocketFallbackStartedThisCall *bool `json:"websocketFallbackStartedThisCall,omitempty"` + // HTTP status of the WebSocket upgrade rejection or WebSocket API error that triggered the HTTP fallback + WebsocketFallbackStatusCode *int32 `json:"websocketFallbackStatusCode,omitempty"` } func (*AssistantUsageData) sessionEventData() {} @@ -2435,6 +2473,10 @@ func (*PermissionCompletedData) Type() SessionEventType { return SessionEventTyp type PermissionRequestedData struct { // Agent mode captured from the owning turn when permission evaluation began. AgentMode *SessionMode `json:"agentMode,omitempty"` + // Delegated agent that owns permission recovery when the owner session multiplexes remote agents. Absent otherwise. + OwnerAgentID *string `json:"ownerAgentId,omitempty"` + // Execution session that owns permission recovery for this request. Absent on historical events. + OwnerSessionID *string `json:"ownerSessionId,omitempty"` // Permission mode captured when evaluation began. Absent on historical events. PermissionMode *PermissionMode `json:"permissionMode,omitempty"` // Details of the permission being requested @@ -3796,6 +3838,8 @@ type CompactionCompleteCompactionTokensUsed struct { OutputTokens *int64 `json:"outputTokens,omitempty"` // Provider captured for the compaction call; absent when attribution is unknown. Provider *ModelProviderRef `json:"provider,omitempty"` + // Number of model requests represented by this aggregated compaction usage + RequestCount *int64 `json:"requestCount,omitempty"` } // Per-request cost and usage data from the CAPI copilot_usage response field @@ -6531,6 +6575,48 @@ const ( ModelCallFinishedOutcomeSuccess ModelCallFinishedOutcome = "success" ) +// Content-Encoding applied to the request body sent on the wire +type ModelCallRequestBodyEncoding string + +const ( + // The request body was sent gzip-compressed. + ModelCallRequestBodyEncodingGzip ModelCallRequestBodyEncoding = "gzip" + // The request body was sent uncompressed (including every WebSocket request). + ModelCallRequestBodyEncodingIdentity ModelCallRequestBodyEncoding = "identity" + // The request body was sent zstd-compressed. + ModelCallRequestBodyEncodingZstd ModelCallRequestBodyEncoding = "zstd" +) + +// Normalized cause of the WebSocket failure that triggered the HTTP fallback +type ModelCallWebSocketFallbackErrorKind string + +const ( + // The peer closed the WebSocket. + ModelCallWebSocketFallbackErrorKindClosedByPeer ModelCallWebSocketFallbackErrorKind = "closed_by_peer" + // The runtime closed the WebSocket itself. + ModelCallWebSocketFallbackErrorKindClosedLocally ModelCallWebSocketFallbackErrorKind = "closed_locally" + // The WebSocket request could not be built. + ModelCallWebSocketFallbackErrorKindConfiguration ModelCallWebSocketFallbackErrorKind = "configuration" + // The peer refused the TCP connection. + ModelCallWebSocketFallbackErrorKindConnectionRefused ModelCallWebSocketFallbackErrorKind = "connection_refused" + // The connection was reset, aborted, or ended mid-stream without a close. + ModelCallWebSocketFallbackErrorKindConnectionReset ModelCallWebSocketFallbackErrorKind = "connection_reset" + // DNS resolution of the WebSocket host failed. + ModelCallWebSocketFallbackErrorKindDns ModelCallWebSocketFallbackErrorKind = "dns" + // The upgrade or request was rejected with an HTTP status. + ModelCallWebSocketFallbackErrorKindHTTPStatus ModelCallWebSocketFallbackErrorKind = "http_status" + // The socket was already closed before it was used. + ModelCallWebSocketFallbackErrorKindNotConnected ModelCallWebSocketFallbackErrorKind = "not_connected" + // A failure that maps onto none of the other kinds. + ModelCallWebSocketFallbackErrorKindOther ModelCallWebSocketFallbackErrorKind = "other" + // A WebSocket protocol violation or an undecodable frame. + ModelCallWebSocketFallbackErrorKindProtocol ModelCallWebSocketFallbackErrorKind = "protocol" + // A WebSocket connect or read timed out. + ModelCallWebSocketFallbackErrorKindTimeout ModelCallWebSocketFallbackErrorKind = "timeout" + // The TLS handshake or a TLS record failed. + ModelCallWebSocketFallbackErrorKindTls ModelCallWebSocketFallbackErrorKind = "tls" +) + // Why a WebSocket-capable model call was carried by the HTTP fallback type ModelCallWebSocketFallbackReason string diff --git a/go/types.go b/go/types.go index 19b263f7f0..6bf2eb3b5b 100644 --- a/go/types.go +++ b/go/types.go @@ -1252,6 +1252,8 @@ func (c MCPHTTPServerConfig) MarshalJSON() ([]byte, error) { // CustomAgentConfig configures a custom agent. type CustomAgentConfig struct { + // Hooks contains declarative actions keyed by event name, active only for this agent. + Hooks map[string][]map[string]any `json:"hooks,omitempty"` // Name is the unique name of the custom agent Name string `json:"name"` // DisplayName is the display name for UI purposes @@ -1323,6 +1325,16 @@ type LargeToolOutputConfig struct { OutputDirectory string `json:"outputDir,omitempty"` } +// ImageGenerationConfig opts in to an authorized Copilot image model. +// Experimental: this API may change or be removed in a future release. +// Omission disables on create/cold resume and preserves resident resume state. +// Not persisted: re-supply after runtime restart. Policy, permissions, offline +// mode, and tool filters still apply. BYOK image generation is not supported. +type ImageGenerationConfig struct { + // Enabled opts in when true and disables image generation when false. + Enabled *bool `json:"enabled,omitempty"` +} + // ToolSearchConfig allows to configure tool search behavior. // Tool search defers tools to keep the model's active tool set small. // To override the tool-search tool's implementation, register a @@ -1467,6 +1479,13 @@ type SkillProvider interface { type SessionConfig struct { // SessionID is an optional custom session ID SessionID string + // Name names the session at creation, as session.name.set would, and is + // validated the same way: non-empty, no leading or trailing whitespace, at + // most 100 UTF-16 units, no control characters, and no double quotes. A name + // also makes the session persist from the start, so it can be listed and + // resumed without ever running a turn. Local sessions only: cloud creation + // rejects it. + Name string // ClientName identifies the application using the SDK. // Included in the User-Agent header for API requests. ClientName string @@ -1505,9 +1524,9 @@ type SessionConfig struct { // of instruction files (AGENTS.md, .github/copilot-instructions.md, etc.) after // successful file views. EnableOnDemandInstructionDiscovery *bool - // EnableFileHooks, when non-nil, controls loading of file-based hooks from - // .github/hooks/. This is separate from the Hooks callback parameter which - // gates SDK hook event registration. + // EnableFileHooks, when non-nil, controls repository-origin file hooks from + // .github/hooks/ and trusted repository-configured roots. It does not control + // explicit HookDirectories or Hooks callbacks. EnableFileHooks *bool // EnableHostGitOperations, when non-nil, controls git operations on the host // filesystem (branch detection, file status, commit history). When false, no @@ -1660,7 +1679,11 @@ type SessionConfig struct { // Agent is the name of the custom agent to activate when the session starts. // Must match the Name of one of the agents in CustomAgents. Agent string - // SkillDirectories is a list of directories to load skills from + // SkillDirectories contains explicit discovery-only skill roots, not filesystem grants. + // Available without EnableConfigDiscovery, subject to skill/managed disables. + // Relative paths resolve once when supplied against the request's effective WorkingDirectory; + // retained roots do not move on cwd changes. Nil and omission are equivalent; + // an empty non-nil list clears only the explicit SDK contribution. SkillDirectories []string // SkillProvider supplies ephemeral SDK-hosted skills for this session. The // provider is never serialized or persisted; re-supply it when resuming. @@ -1668,13 +1691,29 @@ type SessionConfig struct { // Experimental: this API may change or be removed in future SDK or CLI // releases. Implementations must be safe for concurrent calls. SkillProvider SkillProvider `json:"-"` + // AgentDirectories contains explicit discovery-only agent roots, separate from CustomAgents. + // No filesystem grant. Available without EnableConfigDiscovery, subject to source/managed + // disables. Relative paths resolve once when supplied against the request's effective + // WorkingDirectory; retained roots do not move. Nil and omission are equivalent; + // an empty non-nil list clears only the explicit SDK contribution. + AgentDirectories []string + // HookDirectories contains explicit discovery-only file-hook roots, separate from Hooks callbacks. + // No filesystem grant. Available without EnableConfigDiscovery or EnableFileHooks + // (repository-origin hooks only), subject to hook/managed disables. Relative paths resolve + // once when supplied against the request's effective WorkingDirectory; retained roots do not + // move. Nil and omission are equivalent; an empty non-nil list clears only the explicit SDK contribution. + HookDirectories []string // PluginDirectories is a list of local filesystem paths to Open Plugins-format // directories (https://open-plugins.com/) to load for this session. // Relative paths resolve against WorkingDirectory (or the runtime cwd if unset). // Treated as an explicit opt-in: plugin agents and rules load even when // EnableConfigDiscovery is false. PluginDirectories []string - // InstructionDirectories is a list of additional directories to search for custom instruction files + // InstructionDirectories contains explicit discovery-only instruction roots using existing SDK formats. + // No filesystem grant. Available without EnableConfigDiscovery, subject to instruction/managed + // disables. Relative paths resolve once when supplied against the request's effective + // WorkingDirectory; retained roots do not move. Nil and omission are equivalent; + // an empty non-nil list clears only the explicit SDK contribution. InstructionDirectories []string // DisabledSkills is a list of skill names to disable DisabledSkills []string @@ -1693,6 +1732,9 @@ type SessionConfig struct { // defers rarely used tools behind a searchable index. When nil, the runtime // default applies. ToolSearch *ToolSearchConfig + // ImageGeneration is the non-persisted image generation opt-in. + // Experimental: this API may change or be removed in a future release. + ImageGeneration *ImageGenerationConfig // Memory configures the memory feature for the session. When omitted, the // runtime default applies. Memory *MemoryConfiguration @@ -2217,9 +2259,9 @@ type ResumeSessionConfig struct { // EnableOnDemandInstructionDiscovery, when non-nil, controls on-demand discovery // of instruction files after successful file views. EnableOnDemandInstructionDiscovery *bool - // EnableFileHooks, when non-nil, controls loading of file-based hooks from - // .github/hooks/. This is separate from the Hooks callback parameter which - // gates SDK hook event registration. + // EnableFileHooks, when non-nil, controls repository-origin file hooks from + // .github/hooks/ and trusted repository-configured roots. It does not control + // explicit HookDirectories or Hooks callbacks. EnableFileHooks *bool // EnableHostGitOperations, when non-nil, controls git operations on the host // filesystem. @@ -2259,7 +2301,11 @@ type ResumeSessionConfig struct { // Agent is the name of the custom agent to activate when the session starts. // Must match the Name of one of the agents in CustomAgents. Agent string - // SkillDirectories is a list of directories to load skills from + // SkillDirectories contains explicit discovery-only skill roots, not filesystem grants. + // Available without EnableConfigDiscovery, subject to skill/managed disables. + // Relative paths resolve once when supplied against the request's effective WorkingDirectory; + // retained roots do not move on cwd changes. Nil and omission are equivalent; + // an empty non-nil list clears only the explicit SDK contribution. SkillDirectories []string // SkillProvider supplies ephemeral SDK-hosted skills for this resumed // session. Re-supply it on every resume; omitting it unbinds any previous @@ -2268,13 +2314,29 @@ type ResumeSessionConfig struct { // Experimental: this API may change or be removed in future SDK or CLI // releases. Implementations must be safe for concurrent calls. SkillProvider SkillProvider `json:"-"` + // AgentDirectories contains explicit discovery-only agent roots, separate from CustomAgents. + // No filesystem grant. Available without EnableConfigDiscovery, subject to source/managed + // disables. Relative paths resolve once when supplied against the request's effective + // WorkingDirectory; retained roots do not move. Nil and omission are equivalent; + // an empty non-nil list clears only the explicit SDK contribution. + AgentDirectories []string + // HookDirectories contains explicit discovery-only file-hook roots, separate from Hooks callbacks. + // No filesystem grant. Available without EnableConfigDiscovery or EnableFileHooks + // (repository-origin hooks only), subject to hook/managed disables. Relative paths resolve + // once when supplied against the request's effective WorkingDirectory; retained roots do not + // move. Nil and omission are equivalent; an empty non-nil list clears only the explicit SDK contribution. + HookDirectories []string // PluginDirectories is a list of local filesystem paths to Open Plugins-format // directories (https://open-plugins.com/) to load for this session. // Relative paths resolve against WorkingDirectory (or the runtime cwd if unset). // Treated as an explicit opt-in: plugin agents and rules load even when // EnableConfigDiscovery is false. PluginDirectories []string - // InstructionDirectories is a list of additional directories to search for custom instruction files + // InstructionDirectories contains explicit discovery-only instruction roots using existing SDK formats. + // No filesystem grant. Available without EnableConfigDiscovery, subject to instruction/managed + // disables. Relative paths resolve once when supplied against the request's effective + // WorkingDirectory; retained roots do not move. Nil and omission are equivalent; + // an empty non-nil list clears only the explicit SDK contribution. InstructionDirectories []string // DisabledSkills is a list of skill names to disable DisabledSkills []string @@ -2292,6 +2354,9 @@ type ResumeSessionConfig struct { // defers rarely used tools behind a searchable index. When nil, the runtime // default applies. ToolSearch *ToolSearchConfig + // ImageGeneration is the non-persisted image generation opt-in. + // Experimental: this API may change or be removed in a future release. + ImageGeneration *ImageGenerationConfig // Memory configures the memory feature for the session. When omitted, the // runtime default applies. Memory *MemoryConfiguration @@ -2837,6 +2902,7 @@ type SessionLifecycleHandler func(event SessionLifecycleEvent) type createSessionRequest struct { Model string `json:"model,omitempty"` SessionID string `json:"sessionId,omitempty"` + Name string `json:"name,omitempty"` ClientName string `json:"clientName,omitempty"` ReasoningEffort string `json:"reasoningEffort,omitempty"` ReasoningSummary ReasoningSummary `json:"reasoningSummary,omitempty"` @@ -2892,14 +2958,17 @@ type createSessionRequest struct { EnableSessionStore *bool `json:"enableSessionStore,omitempty"` EnableSkills *bool `json:"enableSkills,omitempty"` HasSkillProvider *bool `json:"hasSkillProvider,omitempty"` - SkillDirectories []string `json:"skillDirectories,omitempty"` + SkillDirectories []string `json:"skillDirectories,omitzero"` + AgentDirectories []string `json:"agentDirectories,omitzero"` + HookDirectories []string `json:"hookDirectories,omitzero"` PluginDirectories []string `json:"pluginDirectories,omitempty"` - InstructionDirectories []string `json:"instructionDirectories,omitempty"` + InstructionDirectories []string `json:"instructionDirectories,omitzero"` DisabledSkills []string `json:"disabledSkills,omitempty"` DisabledMCPServers *[]string `json:"disabledMcpServers,omitempty"` InfiniteSessions *InfiniteSessionConfig `json:"infiniteSessions,omitempty"` LargeOutput *LargeToolOutputConfig `json:"largeOutput,omitempty"` ToolSearch *ToolSearchConfig `json:"toolSearch,omitempty"` + ImageGeneration *ImageGenerationConfig `json:"imageGeneration,omitempty"` Memory *MemoryConfiguration `json:"memory,omitempty"` Commands []wireCommand `json:"commands,omitempty"` RequestElicitation *bool `json:"requestElicitation,omitempty"` @@ -2998,14 +3067,17 @@ type resumeSessionRequest struct { CustomAgents []CustomAgentConfig `json:"customAgents,omitempty"` DefaultAgent *DefaultAgentConfig `json:"defaultAgent,omitempty"` Agent string `json:"agent,omitempty"` - SkillDirectories []string `json:"skillDirectories,omitempty"` + SkillDirectories []string `json:"skillDirectories,omitzero"` + AgentDirectories []string `json:"agentDirectories,omitzero"` + HookDirectories []string `json:"hookDirectories,omitzero"` PluginDirectories []string `json:"pluginDirectories,omitempty"` - InstructionDirectories []string `json:"instructionDirectories,omitempty"` + InstructionDirectories []string `json:"instructionDirectories,omitzero"` DisabledSkills []string `json:"disabledSkills,omitempty"` DisabledMCPServers *[]string `json:"disabledMcpServers,omitempty"` InfiniteSessions *InfiniteSessionConfig `json:"infiniteSessions,omitempty"` LargeOutput *LargeToolOutputConfig `json:"largeOutput,omitempty"` ToolSearch *ToolSearchConfig `json:"toolSearch,omitempty"` + ImageGeneration *ImageGenerationConfig `json:"imageGeneration,omitempty"` Memory *MemoryConfiguration `json:"memory,omitempty"` Commands []wireCommand `json:"commands,omitempty"` RequestElicitation *bool `json:"requestElicitation,omitempty"` diff --git a/go/zsession_events.go b/go/zsession_events.go index 0c4775d386..81bb0f2c25 100644 --- a/go/zsession_events.go +++ b/go/zsession_events.go @@ -152,6 +152,7 @@ type ( HumanResponseRecordedResponseResponseKind = rpc.HumanResponseRecordedResponseResponseKind HumanResponseRecordedResponseUserInput = rpc.HumanResponseRecordedResponseUserInput IndexedSearchState = rpc.IndexedSearchState + ManagedPermissionsContext = rpc.ManagedPermissionsContext ManagedPluginProgressPhase = rpc.ManagedPluginProgressPhase ManagedSettingsEnforcedAction = rpc.ManagedSettingsEnforcedAction ManagedSettingsEnforcedEscalation = rpc.ManagedSettingsEnforcedEscalation @@ -191,7 +192,9 @@ type ( ModelCallFinalResultData = rpc.ModelCallFinalResultData ModelCallFinishedData = rpc.ModelCallFinishedData ModelCallFinishedOutcome = rpc.ModelCallFinishedOutcome + ModelCallRequestBodyEncoding = rpc.ModelCallRequestBodyEncoding ModelCallStartData = rpc.ModelCallStartData + ModelCallWebSocketFallbackErrorKind = rpc.ModelCallWebSocketFallbackErrorKind ModelCallWebSocketFallbackReason = rpc.ModelCallWebSocketFallbackReason ModelChangeSource = rpc.ModelChangeSource ModelDeselectedReason = rpc.ModelDeselectedReason @@ -286,6 +289,9 @@ type ( PlanChangedOperation = rpc.PlanChangedOperation PossibleURL = rpc.PossibleURL PromptCacheBreakData = rpc.PromptCacheBreakData + ProviderMonthlyUsage = rpc.ProviderMonthlyUsage + ProviderMonthlyUsageScope = rpc.ProviderMonthlyUsageScope + ProviderMonthlyUsageState = rpc.ProviderMonthlyUsageState ProviderQuotaAccessState = rpc.ProviderQuotaAccessState ProviderQuotaAcquisitionStatus = rpc.ProviderQuotaAcquisitionStatus ProviderQuotaBudgetMetadata = rpc.ProviderQuotaBudgetMetadata @@ -738,6 +744,21 @@ const ( ModelCallFinishedOutcomeError = rpc.ModelCallFinishedOutcomeError ModelCallFinishedOutcomeRejected = rpc.ModelCallFinishedOutcomeRejected ModelCallFinishedOutcomeSuccess = rpc.ModelCallFinishedOutcomeSuccess + ModelCallRequestBodyEncodingGzip = rpc.ModelCallRequestBodyEncodingGzip + ModelCallRequestBodyEncodingIdentity = rpc.ModelCallRequestBodyEncodingIdentity + ModelCallRequestBodyEncodingZstd = rpc.ModelCallRequestBodyEncodingZstd + ModelCallWebSocketFallbackErrorKindClosedByPeer = rpc.ModelCallWebSocketFallbackErrorKindClosedByPeer + ModelCallWebSocketFallbackErrorKindClosedLocally = rpc.ModelCallWebSocketFallbackErrorKindClosedLocally + ModelCallWebSocketFallbackErrorKindConfiguration = rpc.ModelCallWebSocketFallbackErrorKindConfiguration + ModelCallWebSocketFallbackErrorKindConnectionRefused = rpc.ModelCallWebSocketFallbackErrorKindConnectionRefused + ModelCallWebSocketFallbackErrorKindConnectionReset = rpc.ModelCallWebSocketFallbackErrorKindConnectionReset + ModelCallWebSocketFallbackErrorKindDns = rpc.ModelCallWebSocketFallbackErrorKindDns + ModelCallWebSocketFallbackErrorKindHTTPStatus = rpc.ModelCallWebSocketFallbackErrorKindHTTPStatus + ModelCallWebSocketFallbackErrorKindNotConnected = rpc.ModelCallWebSocketFallbackErrorKindNotConnected + ModelCallWebSocketFallbackErrorKindOther = rpc.ModelCallWebSocketFallbackErrorKindOther + ModelCallWebSocketFallbackErrorKindProtocol = rpc.ModelCallWebSocketFallbackErrorKindProtocol + ModelCallWebSocketFallbackErrorKindTimeout = rpc.ModelCallWebSocketFallbackErrorKindTimeout + ModelCallWebSocketFallbackErrorKindTls = rpc.ModelCallWebSocketFallbackErrorKindTls ModelCallWebSocketFallbackReasonAPIError = rpc.ModelCallWebSocketFallbackReasonAPIError ModelCallWebSocketFallbackReasonConnectFailed = rpc.ModelCallWebSocketFallbackReasonConnectFailed ModelCallWebSocketFallbackReasonConnectionUnavailable = rpc.ModelCallWebSocketFallbackReasonConnectionUnavailable @@ -896,6 +917,12 @@ const ( PlanChangedOperationCreate = rpc.PlanChangedOperationCreate PlanChangedOperationDelete = rpc.PlanChangedOperationDelete PlanChangedOperationUpdate = rpc.PlanChangedOperationUpdate + ProviderMonthlyUsageScopeUnknown = rpc.ProviderMonthlyUsageScopeUnknown + ProviderMonthlyUsageScopeUser = rpc.ProviderMonthlyUsageScopeUser + ProviderMonthlyUsageStateAvailable = rpc.ProviderMonthlyUsageStateAvailable + ProviderMonthlyUsageStateNoPolicy = rpc.ProviderMonthlyUsageStateNoPolicy + ProviderMonthlyUsageStateUnavailable = rpc.ProviderMonthlyUsageStateUnavailable + ProviderMonthlyUsageStateUnknown = rpc.ProviderMonthlyUsageStateUnknown ProviderQuotaAccessStateAllowed = rpc.ProviderQuotaAccessStateAllowed ProviderQuotaAccessStateDenied = rpc.ProviderQuotaAccessStateDenied ProviderQuotaAccessStateNotRequired = rpc.ProviderQuotaAccessStateNotRequired @@ -928,6 +955,7 @@ const ( RecommendedAutoTierIntelligence = rpc.RecommendedAutoTierIntelligence RemediationActionAllowSandboxOutbound = rpc.RemediationActionAllowSandboxOutbound RemediationActionReviewSandboxPolicy = rpc.RemediationActionReviewSandboxPolicy + RemediationActionRewindConversation = rpc.RemediationActionRewindConversation RemediationActionShowAccount = rpc.RemediationActionShowAccount RemediationActionSignIn = rpc.RemediationActionSignIn RemediationActionSwitchAccount = rpc.RemediationActionSwitchAccount diff --git a/java/README.md b/java/README.md index f8c0605b90..cb4b2d9c12 100644 --- a/java/README.md +++ b/java/README.md @@ -682,6 +682,19 @@ session.setAutoTier(null).get(); `setModel(SetModelOptions)` accepts the same preference through `SetModelOptions.setAutoTier(...)`, which stages the tier atomically with selecting `auto`. Call `setResetAutoTier(true)` instead to return to provider-default routing; the two options are mutually exclusive. +## Reasoning-summary presentation (experimental) + +Read the current model's reasoning-summary policy through `session.getRpc().model.getReasoningSummaries(...)`. Load the model catalog with `session.getRpc().model.list().get()` first, or accept a result with `ready() == false`. + +```java +import com.github.copilot.generated.rpc.SessionModelGetReasoningSummariesParams; + +var presentation = new SessionModelGetReasoningSummariesParams(null, false); +var policy = session.getRpc().model.getReasoningSummaries(presentation).get(); +``` + +The two constructor arguments are the persisted visibility setting (`null` when unset) and whether the host currently shows summaries (`null` defaults to false). The session wrapper supplies the session ID; callers do not need a placeholder ID. The original three-argument constructor remains available. `visibility()` can be `null` while experiment assignment is pending, and `clearRedundantSetting()` tells the host when its persisted setting matches the model default. + ## Session Store `enableSessionStore` on `SessionConfig` enables the cross-session store for search and retrieval across sessions. When unset in the default `CopilotClientMode.COPILOT_CLI` mode, the runtime default applies (enabled). In `CopilotClientMode.EMPTY` mode, defaults to disabled. diff --git a/java/scripts/codegen/java.test.ts b/java/scripts/codegen/java.test.ts index ba931e7132..1a795e94d6 100644 --- a/java/scripts/codegen/java.test.ts +++ b/java/scripts/codegen/java.test.ts @@ -24,6 +24,64 @@ import { } from "./java.js"; import { RPC_VARIANT_OWNERS } from "./rpc-variant-owners.js"; +test("managed permission contexts share the session-event model in RPC inputs and outputs", () => { + const generated = "../../sdk/src/generated/java/com/github/copilot/generated/"; + for (const name of ["ManagedSettingsResolveResult", "ManagedSettingsPermissionsEvaluateParams"]) { + const source = fs.readFileSync(new URL(`${generated}rpc/${name}.java`, import.meta.url), "utf8"); + assert.match(source, /import com\.github\.copilot\.generated\.ManagedPermissionsContext;/); + assert.match(source, /ManagedPermissionsContext (?:context|permissionsContext)/); + } + assert.equal(fs.existsSync(new URL(`${generated}rpc/ManagedPermissionsContext.java`, import.meta.url)), false); + const event = fs.readFileSync(new URL(`${generated}SessionManagedSettingsResolvedEvent.java`, import.meta.url), "utf8"); + assert.match(event, /ManagedPermissionsContext permissionsContext/); +}); + +test("managed permission requests and results reuse one named operation and kind", async () => { + const files = await renderRpcTypes({ + definitions: { + ManagedPermissionOperation: { + anyOf: [{ + type: "object", + properties: { kind: { type: "string", const: "url" }, url: { type: "string" } }, + required: ["kind", "url"], + }], + }, + ManagedPermissionEvaluation: { + type: "object", + properties: { operation: { $ref: "#/definitions/ManagedPermissionOperation" } }, + required: ["operation"], + }, + }, + server: { + managedSettings: { + permissions: { + evaluate: { + rpcMethod: "managedSettings.permissions.evaluate", + params: { + type: "object", + properties: { operations: { type: "array", items: { $ref: "#/definitions/ManagedPermissionOperation" } } }, + required: ["operations"], + }, + result: { + type: "object", + properties: { results: { type: "array", items: { $ref: "#/definitions/ManagedPermissionEvaluation" } } }, + required: ["results"], + }, + }, + }, + }, + }, + }, {}); + const operation = generatedFile(files, "ManagedPermissionOperation"); + assert.ok(operation); + assert.match(operation, /record ManagedPermissionOperation\(/); + assert.match(operation, /ManagedPermissionOperationKind kind/); + assert.match(operation, /enum ManagedPermissionOperationKind/); + assert.match(operation, /URL\("url"\)/); + assert.match(generatedFile(files, "ManagedSettingsPermissionsEvaluateParams")!, /List operations/); + assert.match(generatedFile(files, "ManagedPermissionEvaluation")!, /ManagedPermissionOperation operation/); +}); + test("wrapper Javadoc preserves the operation contract ahead of parameter documentation", () => { const tree = buildNamespaceTree({ reconcile: { diff --git a/java/scripts/codegen/java.ts b/java/scripts/codegen/java.ts index d327ec4409..9f932aef75 100644 --- a/java/scripts/codegen/java.ts +++ b/java/scripts/codegen/java.ts @@ -1343,7 +1343,34 @@ export function schemaTypeToJava( const name = schema.$ref.replace(/^#\/definitions\//, ""); const resolved = resolution.definitions[name]; if (resolved) { + if ( + name === "ManagedPermissionsContext" + && generatedSessionEventTypeNames.has(name) + && crossSchemaDefinitions.get("session-events.schema.json")?.[name] + ) { + imports.add(`com.github.copilot.generated.${name}`); + return { javaType: name, imports }; + } const directUnion = unionVariants(resolved); + if (name === "ManagedPermissionOperation" && directUnion) { + const variants = resolveAnyOfVariants(directUnion, resolution.definitions); + const [variant] = variants; + if (variants.length === 1 && variant?.properties) { + const kind = variant.properties.kind; + if (!kind || typeof kind !== "object" || typeof kind.const !== "string") { + throw new Error("Expected a string discriminator for ManagedPermissionOperation.kind"); + } + resolution.standaloneTypes.set(name, { + ...resolved, + ...variant, + properties: { + ...variant.properties, + kind: { ...kind, enum: [kind.const] }, + }, + }); + return { javaType: name, imports }; + } + } if (directUnion && ["InstallationReview", "McpInstallationReview", "SkillInstallationReview"].includes(name)) { const variants = resolveAnyOfVariants(directUnion, resolution.definitions); if (variants.length > 1 && findDiscriminator(variants)) { @@ -2371,6 +2398,9 @@ function validateSharedResultOwners(sections: Record[]): void { } function schemaAllowsNull(schema: JSONSchema7): boolean { + if ((schema as Record)["x-opaque-json"] === true && schema.type === undefined) { + return true; + } if (schema.type === "null" || (Array.isArray(schema.type) && schema.type.includes("null"))) { return true; } @@ -2440,6 +2470,18 @@ export function generateRpcClass( } if (localNestedTypes.size > 0 && lines[lines.length - 1] === "") lines.pop(); + if (className === "SessionModelGetReasoningSummariesParams") { + lines.push(``); + lines.push(` /**`); + lines.push(` * Creates host presentation state; the session-scoped wrapper supplies the session ID.`); + lines.push(` *`); + lines.push(` * @param persistedShowReasoning the persisted visibility setting, or null when unset`); + lines.push(` * @param summariesVisible whether summaries are visible, or null for the default`); + lines.push(` */`); + lines.push(` public ${className}(Boolean persistedShowReasoning, Boolean summariesVisible) {`); + lines.push(` this(null, persistedShowReasoning, summariesVisible);`); + lines.push(` }`); + } const legacyConstructor = className === "McpOauthRequiredStaticClientConfig" ? { @@ -3131,6 +3173,10 @@ function wrapperResultClassName(method: RpcMethodNode): string { // Inline result schema with properties let result = originalResult; if (result.$ref) result = resolveRef(result) as JSONSchema7; + if (result.type === "boolean" || result.type === "integer" || result.type === "number" + || (result.type === "string" && !result.enum)) { + return schemaTypeToJava(result, false, rpcResultClassName(method.rpcMethod), "value", new Map()).javaType; + } if ( result && typeof result === "object" && @@ -3145,7 +3191,7 @@ function wrapperResultClassName(method: RpcMethodNode): string { return javaResult.javaType; } - // Free-form object with additionalProperties (e.g., x-opaque-json) → JsonNode + // Boolean maps have a typed value contract; free-form JSON remains a JsonNode. if ( result && typeof result === "object" && @@ -3153,6 +3199,9 @@ function wrapperResultClassName(method: RpcMethodNode): string { result.additionalProperties && !result.properties ) { + if (typeof result.additionalProperties === "object" && result.additionalProperties.type === "boolean") { + return "Map"; + } return "JsonNode"; } @@ -3160,6 +3209,9 @@ function wrapperResultClassName(method: RpcMethodNode): string { } function wrapperResultTypeExpression(resultType: string): string { + if (resultType === "Map") { + return "RpcMapper.INSTANCE.getTypeFactory().constructMapType(Map.class, String.class, Boolean.class)"; + } const listMatch = resultType.match(/^List<([^<>]+)>$/); if (listMatch) { return `RpcMapper.INSTANCE.getTypeFactory().constructCollectionType(List.class, ${javaClassLiteral(listMatch[1])})`; @@ -3182,11 +3234,19 @@ function addWrapperResultImports(resultType: string, allImports: Set, pa return; } + if (resultType === "UUID" || resultType === "OffsetDateTime") { + allImports.add(resultType === "UUID" ? "java.util.UUID" : "java.time.OffsetDateTime"); + return; + } + if (resultType.startsWith("List<")) { allImports.add("java.util.List"); } + if (resultType.startsWith("Map<")) { + allImports.add("java.util.Map"); + } - const builtInTypes = new Set(["Boolean", "Double", "Long", "List", "Object", "String", "Void"]); + const builtInTypes = new Set(["Boolean", "Double", "Long", "List", "Map", "Object", "String", "Void"]); for (const typeName of resultType.match(/\b[A-Z][A-Za-z0-9_]*\b/g) ?? []) { if (!builtInTypes.has(typeName)) { allImports.add(`${packageName}.${typeName}`); diff --git a/java/sdk/pom.xml b/java/sdk/pom.xml index a87f2537ed..bbcaf41d13 100644 --- a/java/sdk/pom.xml +++ b/java/sdk/pom.xml @@ -77,7 +77,7 @@ com.fasterxml.jackson.core jackson-databind - 2.22.2 + 2.22.3 com.fasterxml.jackson.core @@ -87,7 +87,7 @@ com.fasterxml.jackson.datatype jackson-datatype-jsr310 - 2.22.2 + 2.22.3 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/AssistantUsageEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantUsageEvent.java index 75eb6f53f0..a392f290be 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/AssistantUsageEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantUsageEvent.java @@ -97,12 +97,26 @@ public record AssistantUsageEventData( @JsonProperty("rejectedPredictionTokens") Long rejectedPredictionTokens, /** Transport used for this model call (http or websocket) */ @JsonProperty("transport") AssistantUsageTransport transport, - /** Serialized (uncompressed) byte length of the request body. A content-free size signal. */ + /** Serialized (uncompressed) byte length of the request body, before any Content-Encoding. A content-free size signal. */ @JsonProperty("requestBodyBytes") Long requestBodyBytes, + /** Byte length of the request body actually sent on the wire for the attempt that produced this outcome, after any Content-Encoding. Equals requestBodyBytes when the body was not compressed. */ + @JsonProperty("requestBodyWireBytes") Long requestBodyWireBytes, + /** Content-Encoding applied to the request body of the attempt that produced this outcome; identity when uncompressed */ + @JsonProperty("requestBodyEncoding") ModelCallRequestBodyEncoding requestBodyEncoding, /** Why the call was carried by the HTTP fallback of a WebSocket-capable dispatcher; absent when no fallback occurred */ @JsonProperty("websocketFallbackReason") ModelCallWebSocketFallbackReason websocketFallbackReason, /** Milliseconds spent on the WebSocket attempt before falling back to HTTP */ @JsonProperty("websocketFallbackAfterMs") Long websocketFallbackAfterMs, + /** Normalized cause of the WebSocket failure that triggered the HTTP fallback; absent when no fallback occurred or the cause is unknown */ + @JsonProperty("websocketFallbackErrorKind") ModelCallWebSocketFallbackErrorKind websocketFallbackErrorKind, + /** HTTP status of the WebSocket upgrade rejection or WebSocket API error that triggered the HTTP fallback */ + @JsonProperty("websocketFallbackStatusCode") Long websocketFallbackStatusCode, + /** WebSocket close code received before the HTTP fallback */ + @JsonProperty("websocketFallbackCloseCode") Long websocketFallbackCloseCode, + /** Whether a WebSocket reconnect was attempted before falling back to HTTP */ + @JsonProperty("websocketFallbackReconnectAttempted") Boolean websocketFallbackReconnectAttempted, + /** Whether the HTTP fallback started during this model call, rather than being inherited from an earlier call */ + @JsonProperty("websocketFallbackStartedThisCall") Boolean websocketFallbackStartedThisCall, /** Completion ID from the model provider (e.g., chatcmpl-abc123) */ @JsonProperty("apiCallId") String apiCallId, /** GitHub request tracing ID (x-github-request-id header) for server-side log correlation */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/CompactionCompleteCompactionTokensUsed.java b/java/sdk/src/generated/java/com/github/copilot/generated/CompactionCompleteCompactionTokensUsed.java index c325857e59..cd6f610465 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/CompactionCompleteCompactionTokensUsed.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/CompactionCompleteCompactionTokensUsed.java @@ -21,6 +21,8 @@ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public record CompactionCompleteCompactionTokensUsed( + /** Number of model requests represented by this aggregated compaction usage */ + @JsonProperty("requestCount") Long requestCount, /** Whether the compaction's credit amount was reported for all, some, or none of its calls. */ @JsonProperty("aiCreditsStatus") AiCreditsStatus aiCreditsStatus, /** Provider captured for the compaction call; absent when attribution is unknown. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ManagedPermissionsContext.java b/java/sdk/src/generated/java/com/github/copilot/generated/ManagedPermissionsContext.java new file mode 100644 index 0000000000..990ae9ace0 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ManagedPermissionsContext.java @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Reusable managed permission evaluation context. Treat permissions as runtime-owned policy data and retain it verbatim; it includes source-aware composition metadata. This is a snapshot, not a capability or approval token; only use contexts obtained from trusted policy sources. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ManagedPermissionsContext( + /** Runtime-owned composed permissions object. Absent when no permission policy is configured. */ + @JsonProperty("permissions") Object permissions, + /** Policy could not be determined; evaluation must deny every operation. */ + @JsonProperty("failClosed") Boolean failClosed +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureEvent.java index 717523a2ee..9bfa1cc686 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureEvent.java @@ -60,14 +60,28 @@ public record ModelCallFailureEventData( @JsonProperty("transport") ModelCallFailureTransport transport, /** Whether the failure originated from an API response or the request transport */ @JsonProperty("failureKind") ModelCallFailureKind failureKind, - /** Serialized (uncompressed) byte length of the failed request body. A content-free size signal. */ + /** Serialized (uncompressed) byte length of the failed request body, before any Content-Encoding. A content-free size signal. */ @JsonProperty("requestBodyBytes") Long requestBodyBytes, + /** Byte length of the failed request body actually sent on the wire, after any Content-Encoding. Equals requestBodyBytes when the body was not compressed. */ + @JsonProperty("requestBodyWireBytes") Long requestBodyWireBytes, + /** Content-Encoding applied to the failed request body; identity when uncompressed */ + @JsonProperty("requestBodyEncoding") ModelCallRequestBodyEncoding requestBodyEncoding, /** Zero-based orchestrator retry index of the failed attempt */ @JsonProperty("retryAttempt") Long retryAttempt, /** Why the failed call was carried by the HTTP fallback of a WebSocket-capable dispatcher; absent when no fallback occurred */ @JsonProperty("websocketFallbackReason") ModelCallWebSocketFallbackReason websocketFallbackReason, /** Milliseconds spent on the WebSocket attempt before falling back to HTTP */ @JsonProperty("websocketFallbackAfterMs") Long websocketFallbackAfterMs, + /** Normalized cause of the WebSocket failure that triggered the HTTP fallback; absent when no fallback occurred or the cause is unknown */ + @JsonProperty("websocketFallbackErrorKind") ModelCallWebSocketFallbackErrorKind websocketFallbackErrorKind, + /** HTTP status of the WebSocket upgrade rejection or WebSocket API error that triggered the HTTP fallback */ + @JsonProperty("websocketFallbackStatusCode") Long websocketFallbackStatusCode, + /** WebSocket close code received before the HTTP fallback */ + @JsonProperty("websocketFallbackCloseCode") Long websocketFallbackCloseCode, + /** Whether a WebSocket reconnect was attempted before falling back to HTTP */ + @JsonProperty("websocketFallbackReconnectAttempted") Boolean websocketFallbackReconnectAttempted, + /** Whether the HTTP fallback started during this model call, rather than being inherited from an earlier call */ + @JsonProperty("websocketFallbackStartedThisCall") Boolean websocketFallbackStartedThisCall, /** Effective maximum prompt-token limit for the failed call */ @JsonProperty("maxPromptTokens") Long maxPromptTokens, /** Effective maximum output-token limit for the failed call */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallRequestBodyEncoding.java b/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallRequestBodyEncoding.java new file mode 100644 index 0000000000..ad1b96ad9d --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallRequestBodyEncoding.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import javax.annotation.processing.Generated; + +/** + * Content-Encoding applied to the request body sent on the wire + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ModelCallRequestBodyEncoding { + /** The {@code identity} variant. */ + IDENTITY("identity"), + /** The {@code gzip} variant. */ + GZIP("gzip"), + /** The {@code zstd} variant. */ + ZSTD("zstd"); + + private final String value; + ModelCallRequestBodyEncoding(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ModelCallRequestBodyEncoding fromValue(String value) { + for (ModelCallRequestBodyEncoding v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ModelCallRequestBodyEncoding value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallWebSocketFallbackErrorKind.java b/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallWebSocketFallbackErrorKind.java new file mode 100644 index 0000000000..cb429a7d6d --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallWebSocketFallbackErrorKind.java @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import javax.annotation.processing.Generated; + +/** + * Normalized cause of the WebSocket failure that triggered the HTTP fallback + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ModelCallWebSocketFallbackErrorKind { + /** The {@code dns} variant. */ + DNS("dns"), + /** The {@code tls} variant. */ + TLS("tls"), + /** The {@code timeout} variant. */ + TIMEOUT("timeout"), + /** The {@code connection_refused} variant. */ + CONNECTION_REFUSED("connection_refused"), + /** The {@code connection_reset} variant. */ + CONNECTION_RESET("connection_reset"), + /** The {@code closed_by_peer} variant. */ + CLOSED_BY_PEER("closed_by_peer"), + /** The {@code closed_locally} variant. */ + CLOSED_LOCALLY("closed_locally"), + /** The {@code not_connected} variant. */ + NOT_CONNECTED("not_connected"), + /** The {@code http_status} variant. */ + HTTP_STATUS("http_status"), + /** The {@code protocol} variant. */ + PROTOCOL("protocol"), + /** The {@code configuration} variant. */ + CONFIGURATION("configuration"), + /** The {@code other} variant. */ + OTHER("other"); + + private final String value; + ModelCallWebSocketFallbackErrorKind(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ModelCallWebSocketFallbackErrorKind fromValue(String value) { + for (ModelCallWebSocketFallbackErrorKind v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ModelCallWebSocketFallbackErrorKind value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/PermissionRecoveryData.java b/java/sdk/src/generated/java/com/github/copilot/generated/PermissionRecoveryData.java index ed38c3c168..796da04191 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/PermissionRecoveryData.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/PermissionRecoveryData.java @@ -24,6 +24,10 @@ public record PermissionRecoveryData( /** Stable identifier shared by every transition in this recovery episode */ @JsonProperty("episodeId") String episodeId, + /** Root or child execution-session identifier that owns this recovery episode; omitted for legacy snapshots */ + @JsonProperty("ownerSessionId") String ownerSessionId, + /** Subagent identifier that owns this recovery episode when the request was delegated through a parent permission service */ + @JsonProperty("ownerAgentId") String ownerAgentId, /** Current lifecycle state of the recovery episode */ @JsonProperty("status") PermissionRecoveryStatus status, /** Policy selected from the current client's response capability; mode or client changes may update it during recovery */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/PermissionRequestedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/PermissionRequestedEvent.java index dd18d2af2e..5dbb6300af 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/PermissionRequestedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/PermissionRequestedEvent.java @@ -44,6 +44,10 @@ public record PermissionRequestedEventData( @JsonProperty("permissionMode") PermissionMode permissionMode, /** Agent mode captured from the owning turn when permission evaluation began. */ @JsonProperty("agentMode") SessionMode agentMode, + /** Execution session that owns permission recovery for this request. Absent on historical events. */ + @JsonProperty("ownerSessionId") String ownerSessionId, + /** Delegated agent that owns permission recovery when the owner session multiplexes remote agents. Absent otherwise. */ + @JsonProperty("ownerAgentId") String ownerAgentId, /** Neutral risk metadata supplied by the tool host. Consumers may display this value but must not use it to bypass the permission decision. */ @JsonProperty("riskAssessment") Object riskAssessment, /** When true, this permission was already resolved by a permissionRequest hook and requires no client action */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ProviderMonthlyUsage.java b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderMonthlyUsage.java new file mode 100644 index 0000000000..b0c3fdf5b6 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderMonthlyUsage.java @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * An authoritative monthly usage reading for the observation's provider, account, and service. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ProviderMonthlyUsage( + /** Availability of monthly consumption. Missing or unavailable usage must never be interpreted as zero. */ + @JsonProperty("state") ProviderMonthlyUsageState state, + /** Unit of consumedQuantity; independent of the parent observation's balance unit. */ + @JsonProperty("unit") ProviderQuotaUnit unit, + /** Identity scope of the reading within its provider and service. */ + @JsonProperty("scope") ProviderMonthlyUsageScope scope, + /** Nonnegative finite consumption reported by the service, preserving zero and fractions. Present only when state is available; never computed from tokens or balance differences. */ + @JsonProperty("consumedQuantity") Double consumedQuantity, + /** Service read time in UTC RFC 3339, not a ledger reconciliation watermark or confirmation of the latest inference charge. */ + @JsonProperty("queriedAt") String queriedAt, + /** Start of the service-reported monthly billing cycle, in UTC RFC 3339. */ + @JsonProperty("cycleStart") String cycleStart, + /** End of the service-reported monthly billing cycle, in UTC RFC 3339. */ + @JsonProperty("resetOn") String resetOn +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ProviderMonthlyUsageScope.java b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderMonthlyUsageScope.java new file mode 100644 index 0000000000..fb61e9283e --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderMonthlyUsageScope.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import javax.annotation.processing.Generated; + +/** + * Identity scope of a monthly usage reading. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ProviderMonthlyUsageScope { + /** The {@code user} variant. */ + USER("user"), + /** The {@code unknown} variant. */ + UNKNOWN("unknown"); + + private final String value; + ProviderMonthlyUsageScope(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ProviderMonthlyUsageScope fromValue(String value) { + for (ProviderMonthlyUsageScope v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ProviderMonthlyUsageScope value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ProviderMonthlyUsageState.java b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderMonthlyUsageState.java new file mode 100644 index 0000000000..27df08fdb1 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderMonthlyUsageState.java @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import javax.annotation.processing.Generated; + +/** + * Availability of a service's monthly consumption reading. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ProviderMonthlyUsageState { + /** The {@code available} variant. */ + AVAILABLE("available"), + /** The {@code no_policy} variant. */ + NO_POLICY("no_policy"), + /** The {@code unavailable} variant. */ + UNAVAILABLE("unavailable"), + /** The {@code unknown} variant. */ + UNKNOWN("unknown"); + + private final String value; + ProviderMonthlyUsageState(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ProviderMonthlyUsageState fromValue(String value) { + for (ProviderMonthlyUsageState v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ProviderMonthlyUsageState value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaState.java b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaState.java index ddb7fe9aa9..6beeefee69 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaState.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaState.java @@ -49,6 +49,8 @@ public record ProviderQuotaState( @JsonProperty("acquisitionError") String acquisitionError, /** Optional metadata for an authoritative budget. Advisory balances and admission-only observations do not populate this. */ @JsonProperty("budgetMetadata") ProviderQuotaBudgetMetadata budgetMetadata, + /** Service-reported monthly consumption, independent of quota balances and per-call or session cost. Omitted when the service does not report monthly usage. */ + @JsonProperty("monthlyUsage") ProviderMonthlyUsage monthlyUsage, /** Key for the backwards-compatible snapshots projection, when the authoritative budget supports that contract. */ @JsonProperty("compatibilityKey") String compatibilityKey, /** Explicit service admission flag, when reported. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/RemediationAction.java b/java/sdk/src/generated/java/com/github/copilot/generated/RemediationAction.java index 5fba9dfc33..f009f516f1 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/RemediationAction.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/RemediationAction.java @@ -25,7 +25,9 @@ public enum RemediationAction { /** The {@code review_sandbox_policy} variant. */ REVIEW_SANDBOX_POLICY("review_sandbox_policy"), /** The {@code allow_sandbox_outbound} variant. */ - ALLOW_SANDBOX_OUTBOUND("allow_sandbox_outbound"); + ALLOW_SANDBOX_OUTBOUND("allow_sandbox_outbound"), + /** The {@code rewind_conversation} variant. */ + REWIND_CONVERSATION("rewind_conversation"); private final String value; RemediationAction(String value) { this.value = value; } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionErrorEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionErrorEvent.java index 3ef32dfd13..9c6af84ab2 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionErrorEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionErrorEvent.java @@ -34,10 +34,14 @@ public final class SessionErrorEvent extends SessionEvent { @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionErrorEventData( - /** Category of error (e.g., "authentication", "authorization", "quota", "rate_limit", "context_limit", "query") */ + /** Category of error (e.g., "authentication", "authorization", "policy", "quota", "rate_limit", "context_limit", "query") */ @JsonProperty("errorType") String errorType, /** Fine-grained error code from the upstream provider, when available. For `errorType: "rate_limit"`, this is one of the `RateLimitErrorCode` values (e.g., `"user_weekly_rate_limited"`, `"user_global_rate_limited"`, `"rate_limited"`, `"user_model_rate_limited"`, `"integration_rate_limited"`). For `errorType: "quota"`, this is the CAPI quota error code (e.g., `"quota_exceeded"`, `"session_quota_exceeded"`, `"billing_not_configured"`). */ @JsonProperty("errorCode") String errorCode, + /** Provider-defined error type preserved from a structured upstream error envelope, when available (for example, `invalid_request_error`). */ + @JsonProperty("providerErrorType") String providerErrorType, + /** Sanitized provider-defined error parameter preserved from a structured upstream error envelope, when available. */ + @JsonProperty("providerErrorParam") String providerErrorParam, /** Only set on `errorType: "rate_limit"`. When `true`, the runtime will follow this error with an `auto_mode_switch.requested` event (or silently switch if `continueOnAutoMode` is enabled). UI clients can use this flag to suppress duplicate rendering of the rate-limit error when they show their own auto-mode-switch prompt. */ @JsonProperty("eligibleForAutoSwitch") Boolean eligibleForAutoSwitch, /** Human-readable error message */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java index 27f062596c..007e911ef8 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java @@ -40,6 +40,8 @@ public final class SessionManagedSettingsResolvedEvent extends SessionEvent { @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionManagedSettingsResolvedEventData( + /** Reusable retained managed permission policy for managedSettings.permissions.evaluate, including source composition and session-local injection. Provided by live session snapshots; absent in older events. Refresh the context when managed policy changes. */ + @JsonProperty("permissionsContext") ManagedPermissionsContext permissionsContext, /** Channel summary: `server`, `device`, `client`, or `policyHelper` when exactly one channel contributed; `mixed` when multiple channels contributed; otherwise `none`. Consult the per-channel booleans for exact provenance. */ @JsonProperty("source") ManagedSettingsResolvedSource source, /** Whether the server (account/org) managed-settings layer was present */ @@ -50,11 +52,11 @@ public record SessionManagedSettingsResolvedEventData( @JsonProperty("clientManaged") Boolean clientManaged, /** Whether the policy-helper managed-settings layer was present. The policy helper is the weakest channel: it fills keys no enterprise source set and can never replace one. */ @JsonProperty("policyHelperManaged") Boolean policyHelperManaged, - /** Whether managed policy could not be determined (e.g. a failed server fetch) and the session fell back to the fail-closed restriction. When true, restrictions such as disabling bypass-permissions are enforced even though `settings` may be absent. */ + /** Whether managed policy could not be determined (e.g. a failed server fetch) and unresolved-policy safeguards remain active. This does not by itself disable bypass-permissions; `bypassPermissionsDisabled` reports only an explicit policy restriction. */ @JsonProperty("failClosed") Boolean failClosed, /** Whether the effective sandbox policy forces the sandbox on *only* because managed policy could not be determined, rather than because the policy requires it. Lets clients tell a user whose `--no-sandbox` was overridden that the sandbox stayed on as a fail-closed fallback, instead of attributing it to an administrator who set no such policy. */ @JsonProperty("sandboxEnabledByUndeterminedPolicy") Boolean sandboxEnabledByUndeterminedPolicy, - /** Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers, and forced on when `failClosed` is true. */ + /** Whether an explicit enterprise policy restriction disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers; an unresolved policy does not force this on by itself. */ @JsonProperty("bypassPermissionsDisabled") Boolean bypassPermissionsDisabled, /** Whether at least two managed sources supplied permission allowlists, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. */ @JsonProperty("permissionsAllowIntersected") Boolean permissionsAllowIntersected, diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionPermissionRecoveryEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionPermissionRecoveryEvent.java index 56d08f53b4..262bf4578d 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionPermissionRecoveryEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionPermissionRecoveryEvent.java @@ -37,6 +37,10 @@ public final class SessionPermissionRecoveryEvent extends SessionEvent { public record SessionPermissionRecoveryEventData( /** Stable identifier shared by every transition in this recovery episode */ @JsonProperty("episodeId") String episodeId, + /** Root or child execution-session identifier that owns this recovery episode; omitted for legacy snapshots */ + @JsonProperty("ownerSessionId") String ownerSessionId, + /** Subagent identifier that owns this recovery episode when the request was delegated through a parent permission service */ + @JsonProperty("ownerAgentId") String ownerAgentId, /** Current lifecycle state of the recovery episode */ @JsonProperty("status") PermissionRecoveryStatus status, /** Policy selected from the current client's response capability; mode or client changes may update it during recovery */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionQuotaObservationEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionQuotaObservationEvent.java index c7b26660ca..00f710af94 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionQuotaObservationEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionQuotaObservationEvent.java @@ -39,7 +39,7 @@ public final class SessionQuotaObservationEvent extends SessionEvent { @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionQuotaObservationEventData( - /** The admitted provider's state observation. Admission observations never contain quantities or reset/percentage semantics. */ + /** The admitted provider's state observation. Admission balances and percentages remain absent; separate monthly usage may report its own amount and cycle. */ @JsonProperty("observation") ProviderQuotaState observation ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountDescribeAuthInfoParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountDescribeAuthInfoParams.java new file mode 100644 index 0000000000..83a49d849a --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountDescribeAuthInfoParams.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * The authentication identity whose shared wording should be rendered. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record AccountDescribeAuthInfoParams( + /** Identity to describe. */ + @JsonProperty("identity") AuthIdentity identity +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountDescribeAuthInfoResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountDescribeAuthInfoResult.java new file mode 100644 index 0000000000..8fcc083bbb --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountDescribeAuthInfoResult.java @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The wording the runtime uses for an authentication identity. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record AccountDescribeAuthInfoResult( + /** Sign-in banner announcing the authenticated user. */ + @JsonProperty("message") String message, + /** One-line description of the identity, suitable after a verb such as "Switched to". */ + @JsonProperty("description") String description +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountGetGistSupportParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountGetGistSupportParams.java new file mode 100644 index 0000000000..76c1a53037 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountGetGistSupportParams.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * The authentication identity whose gist support should be reported. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record AccountGetGistSupportParams( + /** Identity to evaluate. */ + @JsonProperty("identity") AuthIdentity identity +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountGetGistSupportResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountGetGistSupportResult.java new file mode 100644 index 0000000000..03862487a0 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountGetGistSupportResult.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Whether the identity can create gists, and why not when it cannot. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record AccountGetGistSupportResult( + /** True when the identity can create gists. */ + @JsonProperty("supported") Boolean supported, + /** Machine-readable reason category when unsupported. */ + @JsonProperty("kind") String kind, + /** Human-readable explanation to show where the gist action would have been. */ + @JsonProperty("reason") String reason +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountGetTokenEnvVarResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountGetTokenEnvVarResult.java new file mode 100644 index 0000000000..5e7c2aacf5 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountGetTokenEnvVarResult.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The environment variable supplying a classic personal access token, when one is set. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record AccountGetTokenEnvVarResult( + /** Variable name, for example `GH_TOKEN`. Absent when no classic token is present in the environment. */ + @JsonProperty("name") String name +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountGetWebUrlParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountGetWebUrlParams.java new file mode 100644 index 0000000000..7b4c7686c4 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountGetWebUrlParams.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * A path to resolve against the GitHub host an identity is signed in to. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record AccountGetWebUrlParams( + /** Host-relative path, without a leading slash, for example `settings/copilot/features`. */ + @JsonProperty("path") String path, + /** Identity whose host the path resolves against. Omit to resolve against the public github.com host. */ + @JsonProperty("identity") AuthIdentity identity +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountGetWebUrlResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountGetWebUrlResult.java new file mode 100644 index 0000000000..5aa680cacd --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountGetWebUrlResult.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * An absolute URL on the identity's GitHub host. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record AccountGetWebUrlResult( + /** Absolute URL for the requested path. */ + @JsonProperty("url") String url +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountStatus.java index cb0868ce74..7cdebb3d29 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountStatus.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountStatus.java @@ -31,6 +31,8 @@ public record AccountStatus( @JsonProperty("login") String login, /** The provider kind of this account. */ @JsonProperty("kind") AccountKind kind, + /** Human-readable credential source used to authenticate this account, such as `gh` or `GITHUB_TOKEN`. */ + @JsonProperty("authSource") String authSource, /** Opaque id of the account this one was derived from (e.g. an EMU account's base Entra identity); absent for a root account. Matches the base identity account's selectionId, forming the derivation edge. */ @JsonProperty("derivedFrom") String derivedFrom, /** Whether this is the active account. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentInfo.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentInfo.java index 27fd56a428..17debc1a4c 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentInfo.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentInfo.java @@ -54,6 +54,8 @@ public record AgentInfo( /** Skill names preloaded into this agent's context. Omitted means none. */ @JsonProperty("skills") List skills, /** Authored base prompt for the agent. Runtime prompt assembly may add dynamic context at invocation time. Omitted from `session.agent.list` unless `includePrompt` is true. */ - @JsonProperty("prompt") String prompt + @JsonProperty("prompt") String prompt, + /** Declarative hook actions keyed by event name, executed only while this agent is active. */ + @JsonProperty("hooks") Map> hooks ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsDiscoverRequest.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsDiscoverRequest.java new file mode 100644 index 0000000000..26ead3977a --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsDiscoverRequest.java @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import java.util.Objects; +import javax.annotation.processing.Generated; + +/** + * Optional project paths to include in agent discovery. + *

+ * Required inputs are constructor arguments. Optional inputs have fluent setters. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +public final class AgentsDiscoverRequest { + + /** Optional list of project directory paths to scan for project-scoped agents. When omitted or empty, only user/plugin/remote-independent agents are returned (no project scan). */ + @JsonProperty("projectPaths") + private List projectPaths; + + /** When true, omit the host's agents (the user-level agent directory and all plugin agents), leaving only project and remote agents. For multitenant deployments. */ + @JsonProperty("excludeHostAgents") + private Boolean excludeHostAgents; + + /** Explicit discovery-only agent roots, separate from customAgents and filesystem grants. Available without ambient discovery, subject to managed/source disables. Relative paths resolve against the request's effective working directory when supplied. Null and omission are equivalent; an empty array contributes no explicit roots. */ + @JsonProperty("agentDirectories") + private List agentDirectories; + + /** + * Creates a request with its required inputs. + */ + public AgentsDiscoverRequest() { + } + + /** + * Returns the {@code projectPaths} property. + * + * @return Optional list of project directory paths to scan for project-scoped agents. When omitted or empty, only user/plugin/remote-independent agents are returned (no project scan). + */ + public List getProjectPaths() { + return projectPaths; + } + + /** + * Returns the {@code excludeHostAgents} property. + * + * @return When true, omit the host's agents (the user-level agent directory and all plugin agents), leaving only project and remote agents. For multitenant deployments. + */ + public Boolean getExcludeHostAgents() { + return excludeHostAgents; + } + + /** + * Returns the {@code agentDirectories} property. + * + * @return Explicit discovery-only agent roots, separate from customAgents and filesystem grants. Available without ambient discovery, subject to managed/source disables. Relative paths resolve against the request's effective working directory when supplied. Null and omission are equivalent; an empty array contributes no explicit roots. + */ + public List getAgentDirectories() { + return agentDirectories; + } + + /** + * Sets the {@code projectPaths} property. + * + * @param value Optional list of project directory paths to scan for project-scoped agents. When omitted or empty, only user/plugin/remote-independent agents are returned (no project scan). + * @return this request + */ + public AgentsDiscoverRequest setProjectPaths(List value) { + this.projectPaths = value; + return this; + } + + /** + * Sets the {@code excludeHostAgents} property. + * + * @param value When true, omit the host's agents (the user-level agent directory and all plugin agents), leaving only project and remote agents. For multitenant deployments. + * @return this request + */ + public AgentsDiscoverRequest setExcludeHostAgents(Boolean value) { + this.excludeHostAgents = value; + return this; + } + + /** + * Sets the {@code agentDirectories} property. + * + * @param value Explicit discovery-only agent roots, separate from customAgents and filesystem grants. Available without ambient discovery, subject to managed/source disables. Relative paths resolve against the request's effective working directory when supplied. Null and omission are equivalent; an empty array contributes no explicit roots. + * @return this request + */ + public AgentsDiscoverRequest setAgentDirectories(List value) { + this.agentDirectories = value; + return this; + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetDiscoveryPathsRequest.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetDiscoveryPathsRequest.java new file mode 100644 index 0000000000..fe9123f9ba --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetDiscoveryPathsRequest.java @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import java.util.Objects; +import javax.annotation.processing.Generated; + +/** + * Optional project paths to include when enumerating agent discovery directories. + *

+ * Required inputs are constructor arguments. Optional inputs have fluent setters. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +public final class AgentsGetDiscoveryPathsRequest { + + /** Optional list of project directory paths. When omitted or empty, only the user-level directory is returned. */ + @JsonProperty("projectPaths") + private List projectPaths; + + /** When true, omit the host's user-level agent directory, leaving only project directories. For multitenant deployments (mirrors `discover`'s `excludeHostAgents`). */ + @JsonProperty("excludeHostAgents") + private Boolean excludeHostAgents; + + /** Explicit discovery-only agent roots to enumerate, including configured missing directories. Relative paths resolve against the request's effective working directory when supplied. These are not filesystem grants or preferred creation targets. Null and omission are equivalent. */ + @JsonProperty("agentDirectories") + private List agentDirectories; + + /** + * Creates a request with its required inputs. + */ + public AgentsGetDiscoveryPathsRequest() { + } + + /** + * Returns the {@code projectPaths} property. + * + * @return Optional list of project directory paths. When omitted or empty, only the user-level directory is returned. + */ + public List getProjectPaths() { + return projectPaths; + } + + /** + * Returns the {@code excludeHostAgents} property. + * + * @return When true, omit the host's user-level agent directory, leaving only project directories. For multitenant deployments (mirrors `discover`'s `excludeHostAgents`). + */ + public Boolean getExcludeHostAgents() { + return excludeHostAgents; + } + + /** + * Returns the {@code agentDirectories} property. + * + * @return Explicit discovery-only agent roots to enumerate, including configured missing directories. Relative paths resolve against the request's effective working directory when supplied. These are not filesystem grants or preferred creation targets. Null and omission are equivalent. + */ + public List getAgentDirectories() { + return agentDirectories; + } + + /** + * Sets the {@code projectPaths} property. + * + * @param value Optional list of project directory paths. When omitted or empty, only the user-level directory is returned. + * @return this request + */ + public AgentsGetDiscoveryPathsRequest setProjectPaths(List value) { + this.projectPaths = value; + return this; + } + + /** + * Sets the {@code excludeHostAgents} property. + * + * @param value When true, omit the host's user-level agent directory, leaving only project directories. For multitenant deployments (mirrors `discover`'s `excludeHostAgents`). + * @return this request + */ + public AgentsGetDiscoveryPathsRequest setExcludeHostAgents(Boolean value) { + this.excludeHostAgents = value; + return this; + } + + /** + * Sets the {@code agentDirectories} property. + * + * @param value Explicit discovery-only agent roots to enumerate, including configured missing directories. Relative paths resolve against the request's effective working directory when supplied. These are not filesystem grants or preferred creation targets. Null and omission are equivalent. + * @return this request + */ + public AgentsGetDiscoveryPathsRequest setAgentDirectories(List value) { + this.agentDirectories = value; + return this; + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FeatureFlagAvailability.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FeatureFlagAvailability.java new file mode 100644 index 0000000000..953a52226c --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FeatureFlagAvailability.java @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * The population a feature flag is declared for, before overrides. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +enum FeatureFlagAvailability { + /** The {@code on} variant. */ + ON("on"), + /** The {@code team} variant. */ + TEAM("team"), + /** The {@code staff-and-experimental} variant. */ + STAFF_AND_EXPERIMENTAL("staff-and-experimental"), + /** The {@code staff-or-experimental} variant. */ + STAFF_OR_EXPERIMENTAL("staff-or-experimental"), + /** The {@code staff} variant. */ + STAFF("staff"), + /** The {@code staff-github} variant. */ + STAFF_GITHUB("staff-github"), + /** The {@code staff-microsoft} variant. */ + STAFF_MICROSOFT("staff-microsoft"), + /** The {@code experimental} variant. */ + EXPERIMENTAL("experimental"), + /** The {@code off} variant. */ + OFF("off"); + + private final String value; + FeatureFlagAvailability(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static FeatureFlagAvailability fromValue(String value) { + for (FeatureFlagAvailability v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown FeatureFlagAvailability value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FeatureFlagMetadata.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FeatureFlagMetadata.java new file mode 100644 index 0000000000..49ac33f82f --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FeatureFlagMetadata.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The declared shape of one feature flag, independent of any caller population. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record FeatureFlagMetadata( + /** The flag's name, matching the key it is listed under. */ + @JsonProperty("name") String name, + /** The population the flag is declared for before any environment or user-settings override is applied. */ + @JsonProperty("availability") FeatureFlagAvailability availability, + /** Whether the flag participates in the CAPI sanity suite. */ + @JsonProperty("capiSanity") Boolean capiSanity, + /** Text shown to a user choosing whether to turn the flag on in experimental mode. Absent when the flag is not user-facing. */ + @JsonProperty("experimentalDescription") String experimentalDescription, + /** The online-experiment (ExP) flag this maps to, when the flag is assigned by an experiment rather than declared statically. */ + @JsonProperty("expFlag") String expFlag, + /** Environment variable that overrides this flag's experiment assignment locally in the experiment service, not in `featureFlags.resolve`. Present exactly when `expFlag` is, and derived from it, so a caller reads a local override without reproducing the naming rule. */ + @JsonProperty("expFlagEnvVar") String expFlagEnvVar +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FeatureFlagsMetadataResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FeatureFlagsMetadataResult.java new file mode 100644 index 0000000000..01e738c670 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FeatureFlagsMetadataResult.java @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * Every feature flag this runtime declares, with the shape each one was declared with. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record FeatureFlagsMetadataResult( + /** Declared feature flags keyed by flag name. */ + @JsonProperty("flags") Map flags +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FeatureFlagsResolveParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FeatureFlagsResolveParams.java new file mode 100644 index 0000000000..bf459ff033 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FeatureFlagsResolveParams.java @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * Caller population and overrides for static feature-flag resolution, independent of experiment assignments and active runtime state. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record FeatureFlagsResolveParams( + /** Whether the caller is GitHub or Microsoft staff. */ + @JsonProperty("isStaff") Boolean isStaff, + /** Whether the caller is in an experimental population. */ + @JsonProperty("isExperimental") Boolean isExperimental, + /** Whether the caller belongs to the team population used by feature availability. */ + @JsonProperty("isTeam") Boolean isTeam, + /** GitHub-staff membership. Omit when it was never recorded: a legacy staff account then counts as both staff populations and keeps every staff feature. */ + @JsonProperty("isStaffGithub") Boolean isStaffGitHub, + /** Microsoft-staff membership. Omitted the same way as `isStaffGitHub`. */ + @JsonProperty("isStaffMicrosoft") Boolean isStaffMicrosoft, + /** Resolve against this already-computed flag set rather than each flag's declared availability. Keyed by flag name. */ + @JsonProperty("baseFlags") Map baseFlags, + /** Environment for direct feature-flag overrides and `COPILOT_CLI_ENABLED_FEATURE_FLAGS`. Does not apply `COPILOT_EXP_*` experiment assignments. Defaults to this runtime process's own environment. */ + @JsonProperty("environment") Map environment, + /** User settings to apply, including `enabledFeatureFlags`, legacy `feature_flags.enabled`, and supported convenience settings. */ + @JsonProperty("config") Object config, + /** Flags forced to a value after every other layer, keyed by flag name. */ + @JsonProperty("flagOverrides") Map flagOverrides +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FeatureFlagsResolveStartupGatesParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FeatureFlagsResolveStartupGatesParams.java new file mode 100644 index 0000000000..3a08c2e5b8 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FeatureFlagsResolveStartupGatesParams.java @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * Caller population and startup inputs to resolve named online-experiment gates for. Carries no account: the runtime resolves the assignment for the authentication it already holds. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record FeatureFlagsResolveStartupGatesParams( + /** The feature flags to resolve. A name this runtime does not declare is reported as an error rather than as a disabled flag. */ + @JsonProperty("flags") List flags, + /** Whether the caller is GitHub or Microsoft staff. */ + @JsonProperty("isStaff") Boolean isStaff, + /** Whether the caller is in an experimental population. */ + @JsonProperty("isExperimental") Boolean isExperimental, + /** Whether the caller belongs to a team plan. */ + @JsonProperty("isTeam") Boolean isTeam, + /** GitHub-staff membership. Omit when it was never recorded: a legacy staff account then counts as both staff populations and keeps every staff feature. */ + @JsonProperty("isStaffGithub") Boolean isStaffGitHub, + /** Microsoft-staff membership. Omitted the same way as `isStaffGitHub`. */ + @JsonProperty("isStaffMicrosoft") Boolean isStaffMicrosoft, + /** Environment to read flag overrides from. Defaults to this runtime process's own environment. */ + @JsonProperty("environment") Map environment, + /** User settings document whose `featureFlags` block is layered on top of the resolved set. */ + @JsonProperty("config") Object config, + /** Whether streamer mode is on, which withholds the populations an experiment would otherwise segment on. */ + @JsonProperty("streamerMode") Boolean streamerMode, + /** When this installation first launched, in milliseconds since the Unix epoch. Experiments that segment on installation age read it. Omit when the first launch was never recorded. */ + @JsonProperty("firstLaunchAtMillis") Double firstLaunchAtMillis, + /** Whether the runtime may refresh the cached assignment over the network without an active session. The refresh never blocks this call: the answer comes from the cache either way, so a refresh only affects a later launch. */ + @JsonProperty("refreshWithoutSession") Boolean refreshWithoutSession +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FolderTrustCheckResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FolderTrustCheckResult.java new file mode 100644 index 0000000000..746ad554f5 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FolderTrustCheckResult.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Folder trust check result. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record FolderTrustCheckResult( + /** Whether the folder is trusted */ + @JsonProperty("trusted") Boolean trusted +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ForceLoginOrgsDecision.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ForceLoginOrgsDecision.java new file mode 100644 index 0000000000..24218c69e5 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ForceLoginOrgsDecision.java @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * The verdict of the `forceLoginOrgs` enterprise sign-in pin for one credential. A managed device may pin Copilot to an approved set of GitHub organizations so a developer cannot sign in with a personal — or another enterprise's — account and escape every control that keys off the signed-in identity. A host must refuse to proceed when `allowed` is false; the remaining fields explain the refusal rather than qualifying it. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record ForceLoginOrgsDecision( + /** Whether the client may proceed. True whenever no pin is in force, so an unmanaged host is never blocked. */ + @JsonProperty("allowed") Boolean allowed, + /** Whether a pin applies at all. Distinguishes "allowed because unmanaged" from "allowed because the account is approved", which a diagnostics surface needs and which also decides whether a bring-your-own-key provider may serve inference. */ + @JsonProperty("enforced") Boolean enforced, + /** The approved organization logins, as configured. Enterprise configuration rather than user data, so it is safe to name in a message or a log line. */ + @JsonProperty("requiredOrgs") List requiredOrgs, + /** The approved organization the account matched, when allowed under a pin. */ + @JsonProperty("matchedOrg") String matchedOrg, + /** A stable kebab-case reason code when refused: `org-not-approved`, `unverifiable-credential`, `policy-undetermined`, `policy-invalid`, `not-signed-in`, or `membership-unknown`. Omitted when allowed. Treat an unrecognized code as a refusal rather than mapping it to an allow: a code this host has not been taught about still denied the credential. */ + @JsonProperty("denial") String denial, + /** A developer-facing explanation of the refusal. Omitted when allowed. */ + @JsonProperty("message") String message, + /** Error-level detail from managed-policy discovery — which file failed to load, which field failed validation. Carried on the decision rather than left to the log because a refusal can exit the process before a log sink exists, which would discard the only actionable diagnostic a misconfigured managed device produces. */ + @JsonProperty("policyDiagnostics") List policyDiagnostics +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubOwnersListParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubOwnersListParams.java index 224b85f6dc..fd1489b805 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubOwnersListParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubOwnersListParams.java @@ -27,6 +27,7 @@ record GitHubOwnersListParams( /** Request id from `gitHubOwners.nextRequestId`. An id that was never registered, canceled before use, released after being abandoned, or already used is refused rather than silently running uncancellable. */ @JsonProperty("requestId") Long requestId, /** The credential the listing runs under, carried opaquely because its shape is the host's own and the runtime only resolves a token and a GitHub host from it. No credential travels: this selects one the runtime already holds. */ + @JsonInclude(JsonInclude.Include.ALWAYS) @JsonProperty("authInfo") Object authInfo ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GlobalStateLoadResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GlobalStateLoadResult.java index 0b6dbb9d53..9db799b57e 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GlobalStateLoadResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GlobalStateLoadResult.java @@ -58,8 +58,6 @@ record GlobalStateLoadResult( @JsonProperty("staffUpdateChannelMigrationAt") String staffUpdateChannelMigrationAt, /** Folders the user has marked as trusted. */ @JsonProperty("trustedFolders") List trustedFolders, - /** Whether the user has answered the prompt suggesting they install the desktop app. */ - @JsonProperty("appInstallNudgeResponded") Boolean appInstallNudgeResponded, /** When the Auto-feedback hint was last shown, as an ISO 8601 timestamp. It enforces the once-per-day cap for non-staff users across restarts. */ @JsonProperty("autoFeedbackLastPromptedAt") String autoFeedbackLastPromptedAt ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HooksDiscoverRequest.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HooksDiscoverRequest.java new file mode 100644 index 0000000000..5d76a6d222 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HooksDiscoverRequest.java @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import java.util.Objects; +import javax.annotation.processing.Generated; + +/** + * Optional project paths and host-exclusion behavior for server-scoped hook discovery. + *

+ * Required inputs are constructor arguments. Optional inputs have fluent setters. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +public final class HooksDiscoverRequest { + + /** Optional project directory paths whose trusted repository and project-expanded plugin hooks should be discovered. When omitted or empty, user, managed-policy, and globally enabled installed or explicit plugin hooks are returned without project expansion. */ + @JsonProperty("projectPaths") + private List projectPaths; + + /** When true, omit host-owned user and plugin hook rows and their diagnostics. Managed-policy hooks and trusted repository hooks remain visible, and host disabledHooks still contribute to each remaining row's effective enabled state. This filters sources rather than simulating a host with no settings. */ + @JsonProperty("excludeHostHooks") + private Boolean excludeHostHooks; + + /** Explicit discovery-only file-hook roots, separate from callback hooks and filesystem grants. Available without ambient discovery or repository file-hook enablement, subject to managed/hook disables. Relative paths resolve against the request's effective working directory when supplied. Null and omission are equivalent; an empty array contributes no explicit roots. */ + @JsonProperty("hookDirectories") + private List hookDirectories; + + /** + * Creates a request with its required inputs. + */ + public HooksDiscoverRequest() { + } + + /** + * Returns the {@code projectPaths} property. + * + * @return Optional project directory paths whose trusted repository and project-expanded plugin hooks should be discovered. When omitted or empty, user, managed-policy, and globally enabled installed or explicit plugin hooks are returned without project expansion. + */ + public List getProjectPaths() { + return projectPaths; + } + + /** + * Returns the {@code excludeHostHooks} property. + * + * @return When true, omit host-owned user and plugin hook rows and their diagnostics. Managed-policy hooks and trusted repository hooks remain visible, and host disabledHooks still contribute to each remaining row's effective enabled state. This filters sources rather than simulating a host with no settings. + */ + public Boolean getExcludeHostHooks() { + return excludeHostHooks; + } + + /** + * Returns the {@code hookDirectories} property. + * + * @return Explicit discovery-only file-hook roots, separate from callback hooks and filesystem grants. Available without ambient discovery or repository file-hook enablement, subject to managed/hook disables. Relative paths resolve against the request's effective working directory when supplied. Null and omission are equivalent; an empty array contributes no explicit roots. + */ + public List getHookDirectories() { + return hookDirectories; + } + + /** + * Sets the {@code projectPaths} property. + * + * @param value Optional project directory paths whose trusted repository and project-expanded plugin hooks should be discovered. When omitted or empty, user, managed-policy, and globally enabled installed or explicit plugin hooks are returned without project expansion. + * @return this request + */ + public HooksDiscoverRequest setProjectPaths(List value) { + this.projectPaths = value; + return this; + } + + /** + * Sets the {@code excludeHostHooks} property. + * + * @param value When true, omit host-owned user and plugin hook rows and their diagnostics. Managed-policy hooks and trusted repository hooks remain visible, and host disabledHooks still contribute to each remaining row's effective enabled state. This filters sources rather than simulating a host with no settings. + * @return this request + */ + public HooksDiscoverRequest setExcludeHostHooks(Boolean value) { + this.excludeHostHooks = value; + return this; + } + + /** + * Sets the {@code hookDirectories} property. + * + * @param value Explicit discovery-only file-hook roots, separate from callback hooks and filesystem grants. Available without ambient discovery or repository file-hook enablement, subject to managed/hook disables. Relative paths resolve against the request's effective working directory when supplied. Null and omission are equivalent; an empty array contributes no explicit roots. + * @return this request + */ + public HooksDiscoverRequest setHookDirectories(List value) { + this.hookDirectories = value; + return this; + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionsDiscoverRepoSourcesParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionsDiscoverRepoSourcesParams.java new file mode 100644 index 0000000000..5aee113bf8 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionsDiscoverRepoSourcesParams.java @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Explicit discovery arguments for one location, supplied by the caller rather than derived from a session. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record InstructionsDiscoverRepoSourcesParams( + /** Repository location to discover. An empty string selects no repository scope; cwdPath can still supply working-directory sources. */ + @JsonProperty("location") String location, + /** When true, include the sources a command-line host reads. Hosts that are not the CLI omit it. */ + @JsonProperty("isCli") Boolean isCli, + /** Working directory whose instruction sources should also be included. Omission adds no separate working-directory scope. */ + @JsonProperty("cwdPath") String cwdPath, + /** Copilot configuration directory for user-level sources. An omitted or empty value uses the server's configuration home. */ + @JsonProperty("configDir") String configDir, + /** Extra directories to scan alongside instruction directories configured in the runtime environment. */ + @JsonProperty("additionalDirs") List additionalDirs, + /** When true, also discover instruction files nested below the location. */ + @JsonProperty("enableChildInstructions") Boolean enableChildInstructions +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionsDiscoverRequest.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionsDiscoverRequest.java new file mode 100644 index 0000000000..34175da41b --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionsDiscoverRequest.java @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import java.util.Objects; +import javax.annotation.processing.Generated; + +/** + * Optional project paths to include in instruction discovery. + *

+ * Required inputs are constructor arguments. Optional inputs have fluent setters. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +public final class InstructionsDiscoverRequest { + + /** Optional list of project directory paths to scan for repository/working-directory instruction sources. When omitted or empty, only user-level and plugin instruction sources are returned (no project scan). */ + @JsonProperty("projectPaths") + private List projectPaths; + + /** When true, omit the host's instruction sources (user/home-level files and plugin rules), leaving only repository and working-directory sources. For multitenant deployments. */ + @JsonProperty("excludeHostInstructions") + private Boolean excludeHostInstructions; + + /** Explicit discovery-only instruction roots using existing SDK instruction formats, not filesystem grants. Available without ambient discovery, subject to instruction/managed disables. Relative paths resolve against the request's effective working directory when supplied. Null and omission are equivalent; an empty array contributes no explicit roots. */ + @JsonProperty("instructionDirectories") + private List instructionDirectories; + + /** + * Creates a request with its required inputs. + */ + public InstructionsDiscoverRequest() { + } + + /** + * Returns the {@code projectPaths} property. + * + * @return Optional list of project directory paths to scan for repository/working-directory instruction sources. When omitted or empty, only user-level and plugin instruction sources are returned (no project scan). + */ + public List getProjectPaths() { + return projectPaths; + } + + /** + * Returns the {@code excludeHostInstructions} property. + * + * @return When true, omit the host's instruction sources (user/home-level files and plugin rules), leaving only repository and working-directory sources. For multitenant deployments. + */ + public Boolean getExcludeHostInstructions() { + return excludeHostInstructions; + } + + /** + * Returns the {@code instructionDirectories} property. + * + * @return Explicit discovery-only instruction roots using existing SDK instruction formats, not filesystem grants. Available without ambient discovery, subject to instruction/managed disables. Relative paths resolve against the request's effective working directory when supplied. Null and omission are equivalent; an empty array contributes no explicit roots. + */ + public List getInstructionDirectories() { + return instructionDirectories; + } + + /** + * Sets the {@code projectPaths} property. + * + * @param value Optional list of project directory paths to scan for repository/working-directory instruction sources. When omitted or empty, only user-level and plugin instruction sources are returned (no project scan). + * @return this request + */ + public InstructionsDiscoverRequest setProjectPaths(List value) { + this.projectPaths = value; + return this; + } + + /** + * Sets the {@code excludeHostInstructions} property. + * + * @param value When true, omit the host's instruction sources (user/home-level files and plugin rules), leaving only repository and working-directory sources. For multitenant deployments. + * @return this request + */ + public InstructionsDiscoverRequest setExcludeHostInstructions(Boolean value) { + this.excludeHostInstructions = value; + return this; + } + + /** + * Sets the {@code instructionDirectories} property. + * + * @param value Explicit discovery-only instruction roots using existing SDK instruction formats, not filesystem grants. Available without ambient discovery, subject to instruction/managed disables. Relative paths resolve against the request's effective working directory when supplied. Null and omission are equivalent; an empty array contributes no explicit roots. + * @return this request + */ + public InstructionsDiscoverRequest setInstructionDirectories(List value) { + this.instructionDirectories = value; + return this; + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionsGetDiscoveryPathsRequest.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionsGetDiscoveryPathsRequest.java new file mode 100644 index 0000000000..fc328be97e --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionsGetDiscoveryPathsRequest.java @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import java.util.Objects; +import javax.annotation.processing.Generated; + +/** + * Optional project paths to include when enumerating instruction discovery targets. + *

+ * Required inputs are constructor arguments. Optional inputs have fluent setters. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +public final class InstructionsGetDiscoveryPathsRequest { + + /** Optional list of project directory paths. When omitted or empty, only the user-level targets are returned. */ + @JsonProperty("projectPaths") + private List projectPaths; + + /** When true, omit the host's user-level instruction targets, leaving only repository targets. For multitenant deployments (mirrors `discover`'s `excludeHostInstructions`). */ + @JsonProperty("excludeHostInstructions") + private Boolean excludeHostInstructions; + + /** Explicit discovery-only instruction roots to enumerate, including configured missing directories. Relative paths resolve against the request's effective working directory when supplied. These are not filesystem grants or preferred creation targets. Null and omission are equivalent. */ + @JsonProperty("instructionDirectories") + private List instructionDirectories; + + /** + * Creates a request with its required inputs. + */ + public InstructionsGetDiscoveryPathsRequest() { + } + + /** + * Returns the {@code projectPaths} property. + * + * @return Optional list of project directory paths. When omitted or empty, only the user-level targets are returned. + */ + public List getProjectPaths() { + return projectPaths; + } + + /** + * Returns the {@code excludeHostInstructions} property. + * + * @return When true, omit the host's user-level instruction targets, leaving only repository targets. For multitenant deployments (mirrors `discover`'s `excludeHostInstructions`). + */ + public Boolean getExcludeHostInstructions() { + return excludeHostInstructions; + } + + /** + * Returns the {@code instructionDirectories} property. + * + * @return Explicit discovery-only instruction roots to enumerate, including configured missing directories. Relative paths resolve against the request's effective working directory when supplied. These are not filesystem grants or preferred creation targets. Null and omission are equivalent. + */ + public List getInstructionDirectories() { + return instructionDirectories; + } + + /** + * Sets the {@code projectPaths} property. + * + * @param value Optional list of project directory paths. When omitted or empty, only the user-level targets are returned. + * @return this request + */ + public InstructionsGetDiscoveryPathsRequest setProjectPaths(List value) { + this.projectPaths = value; + return this; + } + + /** + * Sets the {@code excludeHostInstructions} property. + * + * @param value When true, omit the host's user-level instruction targets, leaving only repository targets. For multitenant deployments (mirrors `discover`'s `excludeHostInstructions`). + * @return this request + */ + public InstructionsGetDiscoveryPathsRequest setExcludeHostInstructions(Boolean value) { + this.excludeHostInstructions = value; + return this; + } + + /** + * Sets the {@code instructionDirectories} property. + * + * @param value Explicit discovery-only instruction roots to enumerate, including configured missing directories. Relative paths resolve against the request's effective working directory when supplied. These are not filesystem grants or preferred creation targets. Null and omission are equivalent. + * @return this request + */ + public InstructionsGetDiscoveryPathsRequest setInstructionDirectories(List value) { + this.instructionDirectories = value; + return this; + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionsInitPromptResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionsInitPromptResult.java new file mode 100644 index 0000000000..20eb197498 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionsInitPromptResult.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The prompt the init command runs. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record InstructionsInitPromptResult( + /** Prompt text authoring repository instructions. */ + @JsonProperty("prompt") String prompt +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/LocationPermissionsCleanupResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/LocationPermissionsCleanupResult.java new file mode 100644 index 0000000000..ea8b72d7aa --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/LocationPermissionsCleanupResult.java @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Which stored location permissions were dropped because their directory is gone. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record LocationPermissionsCleanupResult( + /** Location keys whose grants were removed. Empty when every stored location still exists. */ + @JsonProperty("removedLocationKeys") List removedLocationKeys +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedPermissionEvaluation.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedPermissionEvaluation.java new file mode 100644 index 0000000000..40f74da843 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedPermissionEvaluation.java @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ManagedPermissionEvaluation( + /** Original input operation associated with this verdict. */ + @JsonProperty("operation") ManagedPermissionOperation operation, + /** Managed-policy decision only; never bypasses user permissions or other security controls. */ + @JsonProperty("verdict") ManagedPermissionVerdict verdict +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedPermissionOperation.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedPermissionOperation.java new file mode 100644 index 0000000000..ba60365d3c --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedPermissionOperation.java @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ManagedPermissionOperation( + /** Absolute HTTP(S) URL with a host. Invalid URLs reject the entire evaluation batch. */ + @JsonProperty("url") String url, + /** Operation to evaluate. Currently only url is supported. */ + @JsonProperty("kind") ManagedPermissionOperationKind kind +) { + + /** Operation to evaluate. Currently only url is supported. */ + public enum ManagedPermissionOperationKind { + /** The {@code url} variant. */ + URL("url"); + + private final String value; + ManagedPermissionOperationKind(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ManagedPermissionOperationKind fromValue(String value) { + for (ManagedPermissionOperationKind v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ManagedPermissionOperationKind value: " + value); + } + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedPermissionVerdict.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedPermissionVerdict.java new file mode 100644 index 0000000000..1254c36718 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedPermissionVerdict.java @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Managed-policy verdict only: deny blocks the URL, ask requires approval, allow approves under managed policy, and unmanaged leaves the normal consumer permission flow in effect. No verdict bypasses other security controls. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ManagedPermissionVerdict { + /** The {@code deny} variant. */ + DENY("deny"), + /** The {@code ask} variant. */ + ASK("ask"), + /** The {@code allow} variant. */ + ALLOW("allow"), + /** The {@code unmanaged} variant. */ + UNMANAGED("unmanaged"); + + private final String value; + ManagedPermissionVerdict(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ManagedPermissionVerdict fromValue(String value) { + for (ManagedPermissionVerdict v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ManagedPermissionVerdict value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedPluginRetryEntry.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedPluginRetryEntry.java new file mode 100644 index 0000000000..9fb16e5a55 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedPluginRetryEntry.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Retry outcome for one plugin required by managed settings. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ManagedPluginRetryEntry( + /** Plugin spec in "plugin@marketplace" form. */ + @JsonProperty("spec") String spec, + /** What the retry did for this plugin. */ + @JsonProperty("status") ManagedPluginRetryStatus status, + /** Why the plugin was not prepared. Present when `status` is `failed` or `deferred`. */ + @JsonProperty("error") String error +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedPluginRetryStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedPluginRetryStatus.java new file mode 100644 index 0000000000..43a46d2625 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedPluginRetryStatus.java @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * What retrying did for one plugin required by managed settings. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ManagedPluginRetryStatus { + /** The {@code installed} variant. */ + INSTALLED("installed"), + /** The {@code updated} variant. */ + UPDATED("updated"), + /** The {@code already_present} variant. */ + ALREADY_PRESENT("already_present"), + /** The {@code failed} variant. */ + FAILED("failed"), + /** The {@code deferred} variant. */ + DEFERRED("deferred"), + /** The {@code not_required} variant. */ + NOT_REQUIRED("not_required"); + + private final String value; + ManagedPluginRetryStatus(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ManagedPluginRetryStatus fromValue(String value) { + for (ManagedPluginRetryStatus v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ManagedPluginRetryStatus value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsApplyControlsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsApplyControlsParams.java new file mode 100644 index 0000000000..fa5802b6f7 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsApplyControlsParams.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * A user settings document and the effective managed policy whose controlled entries should be stamped onto it. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record ManagedSettingsApplyControlsParams( + /** The user settings document to stamp. Entries the managed layer does not name are returned exactly as supplied. */ + @JsonInclude(JsonInclude.Include.ALWAYS) + @JsonProperty("settings") Object settings, + /** The effective managed policy, as composed by `managedSettings.resolveEffectiveValue`. Omit it on an unmanaged host: the settings then come back unchanged. */ + @JsonProperty("managed") Object managed +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsApplyControlsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsApplyControlsResult.java new file mode 100644 index 0000000000..c5a85c72bb --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsApplyControlsResult.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The user settings with the managed layer's controlled entries stamped on. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record ManagedSettingsApplyControlsResult( + /** The resulting settings document. */ + @JsonProperty("settingsJson") Object settingsJson +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsBypassDisableSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsBypassDisableSource.java new file mode 100644 index 0000000000..f2f64e075f --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsBypassDisableSource.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Which layer disabled bypass-permissions mode. The layer matters because only a server-delivered disable is pinned to an account and can later be lifted by signing in elsewhere. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +enum ManagedSettingsBypassDisableSource { + /** The {@code user} variant. */ + USER("user"), + /** The {@code managed} variant. */ + MANAGED("managed"), + /** The {@code mdm} variant. */ + MDM("mdm"); + + private final String value; + ManagedSettingsBypassDisableSource(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ManagedSettingsBypassDisableSource fromValue(String value) { + for (ManagedSettingsBypassDisableSource v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ManagedSettingsBypassDisableSource value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsBypassPolicyResolution.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsBypassPolicyResolution.java new file mode 100644 index 0000000000..3101cf80fd --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsBypassPolicyResolution.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * What the managed-policy resolution said about bypass-permissions mode. `unknown` is a third answer rather than a missing one, and the caller must send it explicitly when it has no verdict. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +enum ManagedSettingsBypassPolicyResolution { + /** The {@code allowed} variant. */ + ALLOWED("allowed"), + /** The {@code disabled} variant. */ + DISABLED("disabled"), + /** The {@code unknown} variant. */ + UNKNOWN("unknown"); + + private final String value; + ManagedSettingsBypassPolicyResolution(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ManagedSettingsBypassPolicyResolution fromValue(String value) { + for (ManagedSettingsBypassPolicyResolution v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ManagedSettingsBypassPolicyResolution value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsIngestParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsIngestParams.java new file mode 100644 index 0000000000..78e71c35cf --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsIngestParams.java @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Inputs describing the host and account an ingestion should resolve managed policy for. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record ManagedSettingsIngestParams( + /** The policy-helper invocation reason. Defaults to sessionStart. */ + @JsonProperty("policyHelperReason") ManagedSettingsPolicyHelperReason policyHelperReason, + /** Whether to run ingestion. Omit or set false to return an empty result without discovering or fetching policy. */ + @JsonProperty("selfFetch") Boolean selfFetch, + /** The authenticated identity to resolve policy for. Omitted before sign-in, in which case no server layer is fetched. */ + @JsonProperty("authInfo") Object authInfo, + /** The session whose credential store and working directory the policy helper should use. Omit it when no session exists yet and the process-wide store applies. */ + @JsonProperty("sessionId") String sessionId, + /** A device layer supplied by the host instead of discovered from the machine. Pair it with `hasDeviceResponse` so an explicit absence is distinguishable from an omitted override. */ + @JsonProperty("deviceResponse") Object deviceResponse, + /** Whether `deviceResponse` is an override at all. True with a null `deviceResponse` asserts the device layer is empty; false discovers it from the machine. */ + @JsonProperty("hasDeviceResponse") Boolean hasDeviceResponse, + /** Whether to ignore a cached server policy when reading, forcing the resolution to come from a live fetch. */ + @JsonProperty("bypassCacheRead") Boolean bypassCacheRead, + /** How this ingestion may use the on-disk server-policy cache. Defaults to the runtime's own policy when omitted. */ + @JsonProperty("serverPolicyCacheMode") ManagedSettingsServerPolicyCacheMode serverPolicyCacheMode, + /** Whether a configured platform policy helper may be run to produce an enforced helper layer. */ + @JsonProperty("policyHelperEnabled") Boolean policyHelperEnabled, + /** Directory the policy helper runs in when this ingestion is not owned by a registered session. A host resolving policy before its session exists must pass this, or a directory-sensitive helper judges the wrong repository. */ + @JsonProperty("policyHelperWorkingDirectory") String policyHelperWorkingDirectory, + /** User agent to send with the policy fetch. */ + @JsonProperty("userAgent") String userAgent, + /** Name of the embedding client, reported to the managed-settings service as `client_id` so it can refuse to deliver a policy this client is too old to enforce. Omit it and the runtime reports a generic identity; the runtime version sent alongside is the gate that actually matters. */ + @JsonProperty("clientName") String clientName, + /** The host's environment variables, read for cache location and policy overrides. Supplied by the host rather than read from the runtime process so a command that selected its own configuration home is honored. */ + @JsonInclude(JsonInclude.Include.ALWAYS) + @JsonProperty("environment") Object environment, + /** The host's platform, in Node's `process.platform` spelling, used to locate platform-specific cache and policy paths. */ + @JsonProperty("platform") String platform, + /** The host's home directory, used to resolve cache and managed-file locations. */ + @JsonProperty("homeDirectory") String homeDirectory +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsIngestResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsIngestResult.java new file mode 100644 index 0000000000..d5372cf860 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsIngestResult.java @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * The managed policy layers an ingestion resolved, together with the per-layer outcome flags a host needs to decide how much to trust the result. The flags are reported rather than folded into the settings because a host must be able to distinguish an unmanaged device from a managed device whose policy could not be confirmed, and apply its fail-closed gates in the second case. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record ManagedSettingsIngestResult( + /** The effective managed settings composed from every resolved layer. Omitted when no layer set anything, so an unmanaged host locks nothing. */ + @JsonProperty("settings") Object settings, + /** The effective managed permissions layer, when the resolved policy carries one. */ + @JsonProperty("permissions") Object permissions, + /** The server fetch failed and no cached entry stood in for it, so the server layer is unconfirmed and fail-closed gates apply. */ + @JsonProperty("serverFetchFailed") Boolean serverFetchFailed, + /** No usable token was available, so no server fetch was attempted. */ + @JsonProperty("serverTokenUnavailable") Boolean serverTokenUnavailable, + /** The error that made the server fetch fail, when one did. */ + @JsonProperty("serverFetchError") String serverFetchError, + /** How the server layer was resolved. */ + @JsonProperty("serverResolution") ManagedSettingsServerPolicyResolution serverResolution, + /** The server fetch failed in a way a later retry could succeed at, as opposed to a refusal that no retry will change. */ + @JsonProperty("serverRetryableFailure") Boolean serverRetryableFailure, + /** A live fetch failed but a recent cached entry was served in its place, so `serverFetchFailed` stays false. Lets a consumer distinguish a clean fetch from a cache-backed fallback. */ + @JsonProperty("serverCacheFallback") Boolean serverCacheFallback, + /** A server-managed policy is known to exist even though `serverResponse` is intentionally unset. Consumers should OR this with the presence of `serverResponse` when deciding whether the device is managed, so a fresh start during an outage still applies the fail-closed gates instead of treating the device as unmanaged. */ + @JsonProperty("serverSourceFound") Boolean serverSourceFound, + /** The account's server-delivered policy, when one was resolved. */ + @JsonProperty("serverResponse") Object serverResponse, + /** The sandbox object from a cached server policy that a forced refresh declined to serve in full. Folded back into the effective sandbox layer so an outage during a forced refresh still synthesizes the fail-closed sandbox floor from the administrator's known intent. */ + @JsonProperty("serverSandboxFloor") Object serverSandboxFloor, + /** The server fetch failed while a policy demanded a fresh confirmation, which is the strict posture an enterprise opted into rather than an ordinary transient failure. */ + @JsonProperty("serverForcedRefreshFailure") Boolean serverForcedRefreshFailure, + /** The server refused to deliver a policy because this client is too old to enforce it, carrying the user-facing sentence describing the refusal. No retry will help; the remedy is a client update. Gates that exist only because of managed policy should deny on this rather than falling back to their unmanaged default. */ + @JsonProperty("serverClientUpdateRequired") String serverClientUpdateRequired, + /** Minimum client version requested by a successful response. Advisory only; the delivered policy remains active. */ + @JsonProperty("serverClientUpdateRequested") String serverClientUpdateRequested, + /** The synthesized fail-closed permissions layer applied in place of a policy a refused client may not enforce. */ + @JsonProperty("serverPermissionsFloor") Object serverPermissionsFloor, + /** The device layer discovered from the machine, when one exists. */ + @JsonProperty("deviceResponse") Object deviceResponse, + /** The device layer could not be read, so it is unconfirmed rather than known-absent. */ + @JsonProperty("deviceLoadFailed") Boolean deviceLoadFailed, + /** The sandbox floor contributed by the device layer. */ + @JsonProperty("deviceSandboxFloor") Object deviceSandboxFloor, + /** A device-managed policy is known to exist. */ + @JsonProperty("deviceSourceFound") Boolean deviceSourceFound, + /** Whether the device layer's sandbox intent could not be determined, so a consumer must not read its absence as permission to run unsandboxed. */ + @JsonProperty("deviceSandboxUndetermined") Boolean deviceSandboxUndetermined, + /** The layer produced by the platform policy helper, when one ran. */ + @JsonProperty("policyHelperResponse") Object policyHelperResponse, + /** Which channel registered the policy helper that produced the helper layer. */ + @JsonProperty("policyHelperAuthority") ManagedSettingsPolicyHelperAuthority policyHelperAuthority, + /** The policy helper was configured but could not be run or its output could not be read. */ + @JsonProperty("policyHelperLoadFailed") Boolean policyHelperLoadFailed, + /** A policy helper is configured for this host. */ + @JsonProperty("policyHelperConfigured") Boolean policyHelperConfigured, + /** The policy helper's failure must be treated as fail-closed rather than ignored. */ + @JsonProperty("policyHelperFailClosed") Boolean policyHelperFailClosed, + /** Diagnostics emitted while resolving the layers, in the order they occurred. */ + @JsonProperty("logMessages") List logMessages +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsLoadRemoteControlParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsLoadRemoteControlParams.java new file mode 100644 index 0000000000..ff434c10ae --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsLoadRemoteControlParams.java @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Inputs describing the host and account whose managed remote-control policy should be resolved. Mirrors `managedSettings.ingest`, and deliberately carries no retention decision: reading this gate never becomes this runtime process's startup policy. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record ManagedSettingsLoadRemoteControlParams( + /** The policy-helper invocation reason. Defaults to sessionStart. */ + @JsonProperty("policyHelperReason") ManagedSettingsPolicyHelperReason policyHelperReason, + /** Whether to run ingestion. Omit or set false to return an empty result without discovering or fetching policy. */ + @JsonProperty("selfFetch") Boolean selfFetch, + /** The authenticated identity to resolve policy for. Omitted before sign-in, in which case no server layer is fetched. */ + @JsonProperty("authInfo") Object authInfo, + /** A device layer supplied by the host instead of discovered from the machine. Pair it with `hasDeviceResponse` so an explicit absence is distinguishable from an omitted override. */ + @JsonProperty("deviceResponse") Object deviceResponse, + /** Whether `deviceResponse` is an override at all. True with a null `deviceResponse` asserts the device layer is empty; false discovers it from the machine. */ + @JsonProperty("hasDeviceResponse") Boolean hasDeviceResponse, + /** Whether a configured platform policy helper may be run to produce an enforced helper layer. */ + @JsonProperty("policyHelperEnabled") Boolean policyHelperEnabled, + /** User agent to send with the policy fetch. */ + @JsonProperty("userAgent") String userAgent, + /** Name of the embedding client, reported to the managed-settings service as `client_id` so it can refuse to deliver a policy this client is too old to enforce. That refusal is exactly what turns into an explicit `disabled` below, so a host that omits this field forfeits the protection. */ + @JsonProperty("clientName") String clientName, + /** The host's environment variables, read for cache location and policy overrides. */ + @JsonInclude(JsonInclude.Include.ALWAYS) + @JsonProperty("environment") Object environment, + /** The host's platform, in Node's `process.platform` spelling, used to locate platform-specific cache and policy paths. */ + @JsonProperty("platform") String platform, + /** The host's home directory, used to resolve cache and managed-file locations. */ + @JsonProperty("homeDirectory") String homeDirectory +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsLoadRemoteControlResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsLoadRemoteControlResult.java new file mode 100644 index 0000000000..2acf5180c5 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsLoadRemoteControlResult.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The managed remote-control policy in force for an account, if any. An absent policy means the account is unmanaged for this capability, which a host reads as allowed — so absence here is a positive statement that no policy applies, never a stand-in for a resolution that failed. A resolution that cannot be completed is rejected instead. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record ManagedSettingsLoadRemoteControlResult( + /** The account's `remoteControl` policy document. Absent when no managed policy governs remote control for this account. Reported as an explicit disabled policy, rather than as absence, when the service refused to deliver a policy this client is too old to enforce. */ + @JsonProperty("remoteControl") Object remoteControl +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsLogLevel.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsLogLevel.java new file mode 100644 index 0000000000..1775a5db19 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsLogLevel.java @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Severity of a diagnostic emitted while ingesting managed settings. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +enum ManagedSettingsLogLevel { + /** The {@code debug} variant. */ + DEBUG("debug"), + /** The {@code info} variant. */ + INFO("info"), + /** The {@code warning} variant. */ + WARNING("warning"), + /** The {@code error} variant. */ + ERROR("error"); + + private final String value; + ManagedSettingsLogLevel(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ManagedSettingsLogLevel fromValue(String value) { + for (ManagedSettingsLogLevel v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ManagedSettingsLogLevel value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsLogMessage.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsLogMessage.java new file mode 100644 index 0000000000..be859afde3 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsLogMessage.java @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * One diagnostic emitted while ingesting managed settings. Returned rather than logged by the runtime so the host decides where ingestion diagnostics go, which matters because an ingestion runs before a session exists to carry a log sink. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record ManagedSettingsLogMessage( + /** Severity of this diagnostic. */ + @JsonProperty("level") ManagedSettingsLogLevel level, + /** Human-readable diagnostic text. */ + @JsonProperty("message") String message +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsParseClientValueParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsParseClientValueParams.java new file mode 100644 index 0000000000..27effeabae --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsParseClientValueParams.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * The client-supplied managed settings to validate and normalize. Omitting them is valid and yields no policy. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record ManagedSettingsParseClientValueParams( + /** The managed settings a client is supplying for a session it is about to open. Omit them when the client is imposing no policy of its own. */ + @JsonProperty("settings") Object settings +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsParseClientValueResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsParseClientValueResult.java new file mode 100644 index 0000000000..695248ea4a --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsParseClientValueResult.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The accepted managed settings, reduced to the keys a client is allowed to set. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record ManagedSettingsParseClientValueResult( + /** The normalized settings. Omitted when the client supplied none. An accepted document that sets nothing comes back as an empty object, which is distinct from omission. */ + @JsonProperty("settingsJson") Object settingsJson +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsPermissionsEvaluateParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsPermissionsEvaluateParams.java new file mode 100644 index 0000000000..0a5d0c53ab --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsPermissionsEvaluateParams.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import com.github.copilot.generated.ManagedPermissionsContext; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Managed policy context and ordered operations for pure, sessionless evaluation. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ManagedSettingsPermissionsEvaluateParams( + /** Trusted, reusable managed permission policy snapshot; no policy is discovered during evaluation. */ + @JsonProperty("context") ManagedPermissionsContext context, + /** Operations to evaluate, preserving input order and duplicates. An empty batch is valid. */ + @JsonProperty("operations") List operations +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsPermissionsEvaluateResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsPermissionsEvaluateResult.java new file mode 100644 index 0000000000..099a216b48 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsPermissionsEvaluateResult.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Ordered managed permission verdicts and the supplied policy's fail-closed posture. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ManagedSettingsPermissionsEvaluateResult( + /** One verdict per input operation, in the same order, including duplicates. */ + @JsonProperty("results") List results, + /** Managed permission policy could not be determined; every operation is denied. */ + @JsonProperty("failClosed") Boolean failClosed +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsPolicyHelperAuthority.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsPolicyHelperAuthority.java new file mode 100644 index 0000000000..34b33ad56f --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsPolicyHelperAuthority.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Which channel registered the policy helper that produced a helper layer. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +enum ManagedSettingsPolicyHelperAuthority { + /** The {@code device} variant. */ + DEVICE("device"), + /** The {@code server} variant. */ + SERVER("server"); + + private final String value; + ManagedSettingsPolicyHelperAuthority(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ManagedSettingsPolicyHelperAuthority fromValue(String value) { + for (ManagedSettingsPolicyHelperAuthority v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ManagedSettingsPolicyHelperAuthority value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsPolicyHelperReason.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsPolicyHelperReason.java new file mode 100644 index 0000000000..509e98f5e7 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsPolicyHelperReason.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * The operation that requires a policy-helper response. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +enum ManagedSettingsPolicyHelperReason { + /** The {@code sessionStart} variant. */ + SESSIONSTART("sessionStart"), + /** The {@code pluginActivation} variant. */ + PLUGINACTIVATION("pluginActivation"); + + private final String value; + ManagedSettingsPolicyHelperReason(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ManagedSettingsPolicyHelperReason fromValue(String value) { + for (ManagedSettingsPolicyHelperReason v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ManagedSettingsPolicyHelperReason value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsReadDeviceResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsReadDeviceResult.java new file mode 100644 index 0000000000..880044dc83 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsReadDeviceResult.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The device-managed settings layer, with the sandbox floor and the load outcome a host needs to compose it into an effective policy. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record ManagedSettingsReadDeviceResult( + /** Validated, canonical device-managed settings. Omitted when no device policy was discovered, when the discovered document failed validation, or when it validated to an empty object — an empty document carries no policy, and reporting it as present would outrank layers that do. */ + @JsonProperty("settings") Object settings, + /** Sandbox floor read from the discovered document before validation, so a document that is otherwise rejected can still raise the floor it declared. */ + @JsonProperty("sandboxFloor") Object sandboxFloor, + /** Whether discovery or validation failed. A host must treat this as "policy unknown" rather than "no policy": absent settings with this set false mean the device genuinely has none. */ + @JsonProperty("loadFailed") Boolean loadFailed +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveBypassDisableTransitionParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveBypassDisableTransitionParams.java new file mode 100644 index 0000000000..3b56f24c92 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveBypassDisableTransitionParams.java @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * The bypass-permissions state a host holds today, the account now signed in, and what the managed-policy resolution said. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record ManagedSettingsResolveBypassDisableTransitionParams( + /** Which layer currently disables bypass mode. Omit it when nothing does. */ + @JsonProperty("currentSource") ManagedSettingsBypassDisableSource currentSource, + /** The account a `managed` disable is pinned to. Omit it when the current source is not `managed`; it is ignored for the other sources. */ + @JsonProperty("currentManagedIdentity") String currentManagedIdentity, + /** The account signed in now. A `managed` disable pinned to a different account is lifted. */ + @JsonProperty("activeIdentity") String activeIdentity, + /** What the resolution said about bypass mode. Send `unknown` rather than omitting the field when there is no verdict. */ + @JsonProperty("policy") ManagedSettingsBypassPolicyResolution policy +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveBypassDisableTransitionResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveBypassDisableTransitionResult.java new file mode 100644 index 0000000000..2557cd5cb3 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveBypassDisableTransitionResult.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The bypass-permissions state a host should hold after the resolution, and what it must do to get there. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record ManagedSettingsResolveBypassDisableTransitionResult( + /** Which layer disables bypass mode after this resolution. Omitted when none does, which is what a lifted disable looks like. */ + @JsonProperty("source") ManagedSettingsBypassDisableSource source, + /** The account the returned `managed` source is pinned to. Store it alongside `source`; the next call needs it to decide whether an account switch lifts the disable. */ + @JsonProperty("managedIdentity") String managedIdentity, + /** The value to store in the host's own bypass-disabled flag. Omitted means leave it exactly as it is, which is not the same as being told to store `false`. */ + @JsonProperty("setDisabledState") Boolean setDisabledState, + /** Whether a bypass-permissions session already running must be turned off. True only when this resolution newly disables bypass mode, so a host that is already compliant is not interrupted. */ + @JsonProperty("suppressRuntimeAllowAll") Boolean suppressRuntimeAllowAll +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveDeviceForceLoginOrgsGateParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveDeviceForceLoginOrgsGateParams.java new file mode 100644 index 0000000000..75b89cb8bd --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveDeviceForceLoginOrgsGateParams.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * The credential and environment to evaluate the device-channel sign-in pin against. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record ManagedSettingsResolveDeviceForceLoginOrgsGateParams( + /** The authenticated identity to judge. Omitted when signed out, which is the case the device channel exists to serve: it can refuse a first sign-in before any credential exists. */ + @JsonProperty("authInfo") Object authInfo, + /** The host's environment variables. Read for the variables that would make an identity's membership snapshot forgeable, so a credential whose provenance cannot be trusted is refused rather than accepted. */ + @JsonInclude(JsonInclude.Include.ALWAYS) + @JsonProperty("environment") Object environment +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveEffectiveValueParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveEffectiveValueParams.java new file mode 100644 index 0000000000..7e82bf0b3c --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveEffectiveValueParams.java @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * The managed-settings layers to compose into an effective policy. Every layer is optional; omitting all of them yields no effective policy. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record ManagedSettingsResolveEffectiveValueParams( + /** The account's server-delivered org policy, as returned by a managed-settings fetch. Omitted before sign-in or when the fetch has not landed. */ + @JsonProperty("server") Object server, + /** The device layer: native MDM and managed-file settings describing the machine rather than the account. */ + @JsonProperty("mdm") Object mdm, + /** The device sandbox floor, folded into the device layer under `sandbox` before composition. */ + @JsonProperty("deviceSandboxFloor") Object deviceSandboxFloor, + /** The policy-helper layer, an enforced channel sourced from a platform policy helper process. */ + @JsonProperty("policyHelper") Object policyHelper +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveEffectiveValueResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveEffectiveValueResult.java new file mode 100644 index 0000000000..441bb9039f --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveEffectiveValueResult.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The effective managed policy composed from the supplied layers. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record ManagedSettingsResolveEffectiveValueResult( + /** The composed effective managed settings. Omitted when no layer sets anything, so an unmanaged host locks nothing. Carries `sandboxAdvisoryFloorDeclined: true` when an advisory server force-on was dropped because this host cannot run the sandbox. */ + @JsonProperty("settingsJson") Object settingsJson +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveResult.java index 6d9941f4a9..d13b9a555b 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveResult.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.github.copilot.CopilotExperimental; +import com.github.copilot.generated.ManagedPermissionsContext; import java.util.List; import javax.annotation.processing.Generated; @@ -25,6 +26,8 @@ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public record ManagedSettingsResolveResult( + /** Reusable source-composed permission policy for managedSettings.permissions.evaluate. Unlike resolved.settings.permissions, this retains every source's allowlist and default-prompt semantics. Refresh through resolve when account/device policy changes. Absent on runtimes that do not support permission evaluation. */ + @JsonProperty("permissionsContext") ManagedPermissionsContext permissionsContext, /** Printable opaque identity of the account the settings were resolved for, suitable for comparison and storage, not an account selectionId. Absent when no account was available, in which case only device policy is reported. */ @JsonProperty("account") String account, /** Effective managed settings from the device and account (server) channels, in the same shape as `session.managedSettings.get`, excluding session-local injection. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveStartupPolicyParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveStartupPolicyParams.java new file mode 100644 index 0000000000..e0074bb8bc --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveStartupPolicyParams.java @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Inputs describing the host and account a startup policy resolution should run for. Mirrors `managedSettings.ingest`, and adds the decision of whether the resolved policy is retained for this runtime process. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record ManagedSettingsResolveStartupPolicyParams( + /** The policy-helper invocation reason. Defaults to sessionStart. */ + @JsonProperty("policyHelperReason") ManagedSettingsPolicyHelperReason policyHelperReason, + /** Whether to run ingestion. Omit or set false to return an empty result without discovering or fetching policy. */ + @JsonProperty("selfFetch") Boolean selfFetch, + /** The authenticated identity to resolve policy for. Omitted before sign-in, in which case no server layer is fetched and the sign-in pin is evaluated against no credential. */ + @JsonProperty("authInfo") Object authInfo, + /** The session whose credential store, working directory and client kind the resolution should use. Omit it when no session exists yet and the process-wide store applies. A resolution whose session names a credential other than `authInfo` is returned but not retained, so a stale sign-in cannot apply policy for an account that has already been switched away from. */ + @JsonProperty("sessionId") String sessionId, + /** A device layer supplied by the host instead of discovered from the machine. Pair it with `hasDeviceResponse` so an explicit absence is distinguishable from an omitted override. */ + @JsonProperty("deviceResponse") Object deviceResponse, + /** Whether `deviceResponse` is an override at all. True with a null `deviceResponse` asserts the device layer is empty; false discovers it from the machine. */ + @JsonProperty("hasDeviceResponse") Boolean hasDeviceResponse, + /** Whether to ignore a cached server policy when reading, forcing the resolution to come from a live fetch. */ + @JsonProperty("bypassCacheRead") Boolean bypassCacheRead, + /** How this resolution may use the on-disk server-policy cache. Defaults to the runtime's own policy when omitted. Rejected when combined with `bypassCacheRead`, because the two express the same decision and accepting both would leave the caller unsure which one the runtime honored. */ + @JsonProperty("serverPolicyCacheMode") ManagedSettingsServerPolicyCacheMode serverPolicyCacheMode, + /** Whether a configured platform policy helper may be run to produce an enforced helper layer. */ + @JsonProperty("policyHelperEnabled") Boolean policyHelperEnabled, + /** Directory the policy helper runs in when this resolution is not owned by a registered session. A host resolving policy before its session exists must pass this, or a directory-sensitive helper judges the wrong repository. */ + @JsonProperty("policyHelperWorkingDirectory") String policyHelperWorkingDirectory, + /** User agent to send with the policy fetch. */ + @JsonProperty("userAgent") String userAgent, + /** Name of the embedding client, reported to the managed-settings service as `client_id` so it can refuse to deliver a policy this client is too old to enforce. */ + @JsonProperty("clientName") String clientName, + /** The host's environment variables, read for cache location, policy overrides, and the variables that would make a membership snapshot forgeable. */ + @JsonInclude(JsonInclude.Include.ALWAYS) + @JsonProperty("environment") Object environment, + /** The host's platform, in Node's `process.platform` spelling, used to locate platform-specific cache and policy paths. */ + @JsonProperty("platform") String platform, + /** The host's home directory, used to resolve cache and managed-file locations. */ + @JsonProperty("homeDirectory") String homeDirectory, + /** Whether to retain the resolved policy as this runtime process's startup policy, so later work in the process enforces it. Set false to resolve without side effects, which is what a protocol host wants before session creation: its sessions apply the complete policy through the normal lifecycle, and retaining here would let a readiness credential affect unrelated sessions. The returned verdict is identical either way — this decides only whether the process keeps it. */ + @JsonProperty("retainAndApply") Boolean retainAndApply +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveStartupPolicyResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveStartupPolicyResult.java new file mode 100644 index 0000000000..69def35f89 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveStartupPolicyResult.java @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * The startup policy a resolution composed, the per-layer outcome flags a host needs to decide how much to trust it, and the enterprise sign-in verdict for the credential it ran against. The flags are reported rather than folded into the settings because a host must distinguish an unmanaged device from a managed device whose policy could not be confirmed, and apply its fail-closed gates in the second case. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record ManagedSettingsResolveStartupPolicyResult( + /** How the current runtime resolved the server layer. */ + @JsonProperty("serverResolution") ManagedSettingsServerPolicyResolution serverResolution, + /** Advisory minimum client version from a successful server response. */ + @JsonProperty("clientUpdateRequested") String clientUpdateRequested, + /** The account's server-delivered policy, when one was resolved. */ + @JsonProperty("serverResponse") Object serverResponse, + /** The device layer discovered from the machine, when one exists. */ + @JsonProperty("deviceResponse") Object deviceResponse, + /** The sandbox floor contributed by the device layer. */ + @JsonProperty("deviceSandboxFloor") Object deviceSandboxFloor, + /** The layer produced by the platform policy helper, when one ran. */ + @JsonProperty("policyHelperResponse") Object policyHelperResponse, + /** The server fetch failed and no cached entry stood in for it, so the server layer is unconfirmed and fail-closed gates apply. */ + @JsonProperty("serverFetchFailed") Boolean serverFetchFailed, + /** The device layer could not be read, so it is unconfirmed rather than known-absent. */ + @JsonProperty("deviceLoadFailed") Boolean deviceLoadFailed, + /** The policy helper was configured but could not be run, or its output could not be read. */ + @JsonProperty("policyHelperLoadFailed") Boolean policyHelperLoadFailed, + /** Telemetry describing which layers answered and how, for the host to report alongside its own startup events. */ + @JsonProperty("telemetry") Object telemetry, + /** The effective sandbox floor composed across every layer, which a host must apply rather than its own default. */ + @JsonProperty("sandboxFloor") Object sandboxFloor, + /** Some layer of the policy could not be confirmed, so its absence must not be read as permission. Folds in a failed server fetch as well as an unreadable device layer. */ + @JsonProperty("policyUndetermined") Boolean policyUndetermined, + /** The permissions layer specifically could not be confirmed, so permission gates apply their fail-closed posture. */ + @JsonProperty("permissionPolicyUndetermined") Boolean permissionPolicyUndetermined, + /** A managed policy is known to apply to this host, from a discovered source or a resolved document. */ + @JsonProperty("managedPolicyPresent") Boolean managedPolicyPresent, + /** The sandbox was enabled because policy could not be confirmed rather than because an administrator asked for it. Lets a host explain a sandbox the user did not configure. */ + @JsonProperty("sandboxEnabledByUndeterminedPolicy") Boolean sandboxEnabledByUndeterminedPolicy, + /** The enterprise sign-in verdict for the credential this resolution ran against, evaluated over the effective policy so a server-delivered pin is honored alongside a device one. Computed on every resolution, including one that was not retained, so a host that declines to apply policy still cannot proceed past a pin. */ + @JsonProperty("forceLoginOrgs") ForceLoginOrgsDecision forceLoginOrgs, + /** The server refused to deliver a policy because this client is too old to enforce it, carrying the user-facing sentence describing the refusal. No retry will help; the remedy is a client update. */ + @JsonProperty("clientUpdateRequired") String clientUpdateRequired, + /** Warnings raised while resolving, in the order they occurred. */ + @JsonProperty("warnings") List warnings +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolvedData.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolvedData.java index 2020e9d434..aaef086d58 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolvedData.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolvedData.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.github.copilot.CopilotExperimental; +import com.github.copilot.generated.ManagedPermissionsContext; import java.util.List; import javax.annotation.processing.Generated; @@ -26,6 +27,8 @@ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public record ManagedSettingsResolvedData( + /** Reusable retained managed permission policy for managedSettings.permissions.evaluate, including source composition and session-local injection. Provided by live session snapshots; absent in older events. Refresh the context when managed policy changes. */ + @JsonProperty("permissionsContext") ManagedPermissionsContext permissionsContext, /** Channel summary: `server`, `device`, `client`, or `policyHelper` when exactly one channel contributed; `mixed` when multiple channels contributed; otherwise `none`. Consult the per-channel booleans for exact provenance. */ @JsonProperty("source") ManagedSettingsResolvedSource source, /** Whether the server (account/org) managed-settings layer was present */ @@ -36,11 +39,11 @@ public record ManagedSettingsResolvedData( @JsonProperty("clientManaged") Boolean clientManaged, /** Whether the policy-helper managed-settings layer was present. The policy helper is the weakest channel: it fills keys no enterprise source set and can never replace one. */ @JsonProperty("policyHelperManaged") Boolean policyHelperManaged, - /** Whether managed policy could not be determined (e.g. a failed server fetch) and the session fell back to the fail-closed restriction. When true, restrictions such as disabling bypass-permissions are enforced even though `settings` may be absent. */ + /** Whether managed policy could not be determined (e.g. a failed server fetch) and unresolved-policy safeguards remain active. This does not by itself disable bypass-permissions; `bypassPermissionsDisabled` reports only an explicit policy restriction. */ @JsonProperty("failClosed") Boolean failClosed, /** Whether the effective sandbox policy forces the sandbox on *only* because managed policy could not be determined, rather than because the policy requires it. Lets clients tell a user whose `--no-sandbox` was overridden that the sandbox stayed on as a fail-closed fallback, instead of attributing it to an administrator who set no such policy. */ @JsonProperty("sandboxEnabledByUndeterminedPolicy") Boolean sandboxEnabledByUndeterminedPolicy, - /** Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers, and forced on when `failClosed` is true. */ + /** Whether an explicit enterprise policy restriction disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers; an unresolved policy does not force this on by itself. */ @JsonProperty("bypassPermissionsDisabled") Boolean bypassPermissionsDisabled, /** Whether at least two managed sources supplied permission allowlists, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. */ @JsonProperty("permissionsAllowIntersected") Boolean permissionsAllowIntersected, diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsRetainPolicyParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsRetainPolicyParams.java new file mode 100644 index 0000000000..5ea352e817 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsRetainPolicyParams.java @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * The managed layers to retain as this process's startup policy, with the outcome of fetching each one. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record ManagedSettingsRetainPolicyParams( + /** The resolved server policy. A known source can lack a response after a forced-refresh failure. */ + @JsonProperty("serverResponse") Object serverResponse, + /** The device/MDM policy layer. */ + @JsonProperty("deviceResponse") Object deviceResponse, + /** Sandbox floor declared by the device layer, carried separately so a document that failed validation can still raise the floor. */ + @JsonProperty("deviceSandboxFloor") Object deviceSandboxFloor, + /** Whether a device policy source demonstrably exists. This, not a read error, is the evidence a device is managed, so a fail-closed policy gates on it. */ + @JsonProperty("deviceSourceFound") Boolean deviceSourceFound, + /** Whether any managed policy source exists, independently of fetch failures. */ + @JsonProperty("managedPolicyPresent") Boolean managedPolicyPresent, + /** Whether the server policy source exists, even when its response is unavailable. */ + @JsonProperty("serverSourceFound") Boolean serverSourceFound, + /** Whether a required fresh server-policy confirmation failed. */ + @JsonProperty("serverForcedRefreshFailure") Boolean serverForcedRefreshFailure, + /** Preserve the retained server layer during a device-only resolution. */ + @JsonProperty("preserveServerState") Boolean preserveServerState, + /** Whether the device sandbox intent is unreadable independently of its other settings. */ + @JsonProperty("deviceSandboxUndetermined") Boolean deviceSandboxUndetermined, + /** Whether the shared sandbox policy is undetermined. Diagnostic errors alone do not imply this flag. */ + @JsonProperty("sandboxFloorUndetermined") Boolean sandboxFloorUndetermined, + /** Whether permission policy is undetermined. Sandbox-only uncertainty does not imply this flag. */ + @JsonProperty("permissionPolicyUndetermined") Boolean permissionPolicyUndetermined, + /** Whether uncertainty alone enabled the shared sandbox floor. */ + @JsonProperty("sandboxEnabledByUndeterminedPolicy") Boolean sandboxEnabledByUndeterminedPolicy, + /** Shared server/device sandbox floor, excluding session-local helper policy. */ + @JsonProperty("sharedSandboxFloor") Object sharedSandboxFloor, + /** Whether the server fetch failed. Report it honestly: true keeps the already-retained server layer rather than replacing it with an absent one. */ + @JsonProperty("serverFetchFailed") Boolean serverFetchFailed, + /** Whether the device load failed. Report it honestly, for the same reason as `serverFetchFailed`. */ + @JsonProperty("deviceLoadFailed") Boolean deviceLoadFailed, + /** The policy helper's response: the third managed layer that a process owning its startup policy resolves. It is retained with the other two so that every ambient read in this process, including the mutation guards and the governed marketplace allowlist that the `plugins.*` handlers derive, sees the whole policy the process fetched. A session-owning process omits it, because it resolves a helper per session against that session's working directory. */ + @JsonProperty("policyHelperResponse") Object policyHelperResponse +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsServerPolicyCacheMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsServerPolicyCacheMode.java new file mode 100644 index 0000000000..7258e376b5 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsServerPolicyCacheMode.java @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * How an ingestion may use the on-disk server-policy cache. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +enum ManagedSettingsServerPolicyCacheMode { + /** The {@code preferFresh} variant. */ + PREFERFRESH("preferFresh"), + /** The {@code cacheOnly} variant. */ + CACHEONLY("cacheOnly"), + /** The {@code fallbackOnly} variant. */ + FALLBACKONLY("fallbackOnly"), + /** The {@code disabled} variant. */ + DISABLED("disabled"); + + private final String value; + ManagedSettingsServerPolicyCacheMode(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ManagedSettingsServerPolicyCacheMode fromValue(String value) { + for (ManagedSettingsServerPolicyCacheMode v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ManagedSettingsServerPolicyCacheMode value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsServerPolicyResolution.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsServerPolicyResolution.java new file mode 100644 index 0000000000..8315739063 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsServerPolicyResolution.java @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * How the server layer of an ingestion was resolved. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +enum ManagedSettingsServerPolicyResolution { + /** The {@code live} variant. */ + LIVE("live"), + /** The {@code freshCache} variant. */ + FRESHCACHE("freshCache"), + /** The {@code cacheMiss} variant. */ + CACHEMISS("cacheMiss"), + /** The {@code fallbackCache} variant. */ + FALLBACKCACHE("fallbackCache"), + /** The {@code failOpen} variant. */ + FAILOPEN("failOpen"), + /** The {@code failClosed} variant. */ + FAILCLOSED("failClosed"), + /** The {@code skippedNoToken} variant. */ + SKIPPEDNOTOKEN("skippedNoToken"); + + private final String value; + ManagedSettingsServerPolicyResolution(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ManagedSettingsServerPolicyResolution fromValue(String value) { + for (ManagedSettingsServerPolicyResolution v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ManagedSettingsServerPolicyResolution value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsValidateParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsValidateParams.java index 32879f689c..40a4e02992 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsValidateParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsValidateParams.java @@ -25,6 +25,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) public record ManagedSettingsValidateParams( /** The document to validate: a JSON object, or a string containing the document's JSON text. Preview documents are limited to 1 MiB and 64 levels of nesting, a stricter resource limit than delivered-policy parsing; violations are returned as diagnostics. */ + @JsonInclude(JsonInclude.Include.ALWAYS) @JsonProperty("content") Object content, /** Channel the document is meant for (`device`, `server`, or `policyHelper`). Some keys are only honored in some channels; for example, a `policyHelper` registration is ignored in policy-helper output. When omitted, no channel-specific checks run. */ @JsonProperty("layer") String layer diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpListBuiltinServerNamesResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpListBuiltinServerNamesResult.java new file mode 100644 index 0000000000..2a65a679d9 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpListBuiltinServerNamesResult.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Names of the MCP servers the runtime supplies itself. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record McpListBuiltinServerNamesResult( + /** Every built-in server name. A host disabling the built-ins wholesale suppresses exactly this set. */ + @JsonProperty("names") List names +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpRegistryCancelParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpRegistryCancelParams.java new file mode 100644 index 0000000000..0a12a5c26b --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpRegistryCancelParams.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Registry search to abandon. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record McpRegistryCancelParams( + /** Request ID from `mcp.registry.allocateRequestId` that the search uses. */ + @JsonProperty("requestId") Long requestId +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpRegistryCancelResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpRegistryCancelResult.java new file mode 100644 index 0000000000..c7f581f4cc --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpRegistryCancelResult.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Whether the cancel reached a live search. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record McpRegistryCancelResult( + /** True when the cancel stopped a running search. False for unknown or reclaimed IDs, completed or canceled searches, and unused reservations. The cancel releases an unused reservation. */ + @JsonProperty("canceled") Boolean canceled +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpRegistryRequestIdResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpRegistryRequestIdResult.java new file mode 100644 index 0000000000..53234b3598 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpRegistryRequestIdResult.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Request id naming a cancellable registry search. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record McpRegistryRequestIdResult( + /** Request ID for `mcp.registry.search` and `mcp.registry.cancel`. It serves one search. Allocation can reclaim unused IDs at the 1,024-ID reservation limit. */ + @JsonProperty("requestId") Long requestId +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpRegistrySearchParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpRegistrySearchParams.java new file mode 100644 index 0000000000..11a06de374 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpRegistrySearchParams.java @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Registry search terms, the credential to search under, and the request id that makes the search cancellable. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record McpRegistrySearchParams( + /** Request ID from `mcp.registry.allocateRequestId`. The search refuses unknown, reclaimed, or canceled IDs and IDs that another search or request already uses. */ + @JsonProperty("requestId") Long requestId, + /** The credential the search runs under, carried opaquely. Hosts usually send credential-free `AuthIdentity`; a `token` identity can be resolved only when it embeds a token, while `env` and `gh-cli` identities can use a token embedded in the request first. */ + @JsonInclude(JsonInclude.Include.ALWAYS) + @JsonProperty("authInfo") Object authInfo, + /** Free-text query. Omitted or empty asks the registry for its top servers rather than searching. A value that is not a string is refused. */ + @JsonProperty("query") String query, + /** Repository used for the policy lookup, as `owner/name`. The policy selects the registry URL and whether the user token goes to the registry. The registry receives this repository only when the policy entry lists it as required context. A value that is not a string is refused. */ + @JsonProperty("repository") String repository, + /** Maximum number of servers to return. */ + @JsonProperty("limit") Long limit +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpRegistrySearchResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpRegistrySearchResult.java new file mode 100644 index 0000000000..2682402d76 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpRegistrySearchResult.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Servers selected from the registry response. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record McpRegistrySearchResult( + /** The server objects, carried opaquely. The runtime follows pages, keeps the newest entry per server name, cuts the list to `limit`, and sorts an empty-query result by GitHub stars. Each server object remains unchanged because the registry owns that shape. */ + @JsonProperty("servers") Object servers +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpShouldExcludeGitHubToolsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpShouldExcludeGitHubToolsParams.java new file mode 100644 index 0000000000..bb435663bf --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpShouldExcludeGitHubToolsParams.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * The session tool filters that decide whether the session still has a shell to run `gh` with. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record McpShouldExcludeGitHubToolsParams( + /** The session's tool allowlist, when it set one. Omitted means the session constrains nothing this way. */ + @JsonProperty("availableTools") List availableTools, + /** The session's tool denylist, when it set one. Omitted means the session constrains nothing this way. */ + @JsonProperty("excludedTools") List excludedTools, + /** How the allowlist and denylist combine when both are set. Omitted means the default every session gets, so a caller that never chose a precedence is answered as its sessions behave. */ + @JsonProperty("toolFilterPrecedence") OptionsUpdateToolFilterPrecedence toolFilterPrecedence +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpShouldExcludeGitHubToolsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpShouldExcludeGitHubToolsResult.java new file mode 100644 index 0000000000..05f92ab309 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpShouldExcludeGitHubToolsResult.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Whether the gh-replaceable GitHub MCP tools may be clipped for the session described by the request. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record McpShouldExcludeGitHubToolsResult( + /** True only when both halves hold: the session's filters still reach the platform shell tool, and the host actually has the `gh` those tools would be replaced by. Feed it straight back as the `excludeGhReplaceableTools` build option. */ + @JsonProperty("excludeGhReplaceableTools") Boolean excludeGhReplaceableTools +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionRecoveryData.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionRecoveryData.java index e4c502116d..990349ebaf 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionRecoveryData.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionRecoveryData.java @@ -24,6 +24,10 @@ public record PermissionRecoveryData( /** Stable identifier shared by every transition in this recovery episode */ @JsonProperty("episodeId") String episodeId, + /** Root or child execution-session identifier that owns this recovery episode; omitted for legacy snapshots */ + @JsonProperty("ownerSessionId") String ownerSessionId, + /** Subagent identifier that owns this recovery episode when the request was delegated through a parent permission service */ + @JsonProperty("ownerAgentId") String ownerAgentId, /** Current lifecycle state of the recovery episode */ @JsonProperty("status") PermissionRecoveryStatus status, /** Policy selected from the current client's response capability; mode or client changes may update it during recovery */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsEvaluateManagedPolicyParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsEvaluateManagedPolicyParams.java new file mode 100644 index 0000000000..65a15a044e --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsEvaluateManagedPolicyParams.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * The verbatim managed `permissions` slice to interpret. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record PermissionsEvaluateManagedPolicyParams( + /** The `permissions` object from a managed settings layer, for example `{"disableBypassPermissionsMode":"disable"}`. Omitted or null means no managed policy applies. */ + @JsonProperty("permissions") Object permissions +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsEvaluateManagedPolicyResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsEvaluateManagedPolicyResult.java new file mode 100644 index 0000000000..2690dad76f --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsEvaluateManagedPolicyResult.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The policy questions a host asks before granting elevated permissions. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record PermissionsEvaluateManagedPolicyResult( + /** True when managed policy forbids bypass-permissions mode, so allow-all style flags must not take effect. */ + @JsonProperty("bypassPermissionsDisabled") Boolean bypassPermissionsDisabled, + /** True when a managed policy exists but this build could not determine its meaning. Hosts fail closed on it rather than assuming permission was granted. */ + @JsonProperty("policyUndetermined") Boolean policyUndetermined, + /** True when the managed layer supplies permission rules that the session must enforce. */ + @JsonProperty("permissionRulesActive") Boolean permissionRulesActive +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsFolderTrustCheckTrustParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsFolderTrustCheckTrustParams.java new file mode 100644 index 0000000000..5fadba2eec --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsFolderTrustCheckTrustParams.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * A folder to check against a persisted trusted-folders list, and the configuration directory holding that list. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record PermissionsFolderTrustCheckTrustParams( + /** Folder to check. A folder counts as trusted when it is itself listed, when an ancestor is listed, or when its git root is listed. */ + @JsonProperty("path") String path, + /** Configuration directory to read the trusted-folders list from. Omit it to read the default location; supply it when the host has redirected its configuration, or the check answers from a list the host is not using. */ + @JsonProperty("configDir") String configDir +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PersistenceWriteRawJsonLockedParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PersistenceWriteRawJsonLockedParams.java new file mode 100644 index 0000000000..eff9c61d51 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PersistenceWriteRawJsonLockedParams.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * A complete JSON snapshot to write under the runtime's per-path file lock. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record PersistenceWriteRawJsonLockedParams( + /** Absolute path of the file to replace. Parent directories are created when missing. */ + @JsonProperty("path") String path, + /** The serialized JSON document to write. It replaces the file's contents entirely; nothing is merged. */ + @JsonProperty("dataJson") String dataJson, + /** Comment lines to emit above the document, for a format that tolerates them. Omit for strict JSON. */ + @JsonProperty("fileHeader") String fileHeader +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PersistenceWriteRawJsonLockedResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PersistenceWriteRawJsonLockedResult.java new file mode 100644 index 0000000000..5f8616d042 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PersistenceWriteRawJsonLockedResult.java @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The outcome of a locked snapshot write. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record PersistenceWriteRawJsonLockedResult( + /** Whether the file now holds the requested contents. */ + @JsonProperty("success") Boolean success, + /** Why the write failed. Absent when `success` is true. A lock the runtime could not acquire in time reports here rather than raising. */ + @JsonProperty("error") String error +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderMonthlyUsage.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderMonthlyUsage.java new file mode 100644 index 0000000000..355f7e38f0 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderMonthlyUsage.java @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * An authoritative monthly usage reading for the observation's provider, account, and service. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ProviderMonthlyUsage( + /** Availability of monthly consumption. Missing or unavailable usage must never be interpreted as zero. */ + @JsonProperty("state") ProviderMonthlyUsageState state, + /** Unit of consumedQuantity; independent of the parent observation's balance unit. */ + @JsonProperty("unit") ProviderQuotaUnit unit, + /** Identity scope of the reading within its provider and service. */ + @JsonProperty("scope") ProviderMonthlyUsageScope scope, + /** Nonnegative finite consumption reported by the service, preserving zero and fractions. Present only when state is available; never computed from tokens or balance differences. */ + @JsonProperty("consumedQuantity") Double consumedQuantity, + /** Service read time in UTC RFC 3339, not a ledger reconciliation watermark or confirmation of the latest inference charge. */ + @JsonProperty("queriedAt") String queriedAt, + /** Start of the service-reported monthly billing cycle, in UTC RFC 3339. */ + @JsonProperty("cycleStart") String cycleStart, + /** End of the service-reported monthly billing cycle, in UTC RFC 3339. */ + @JsonProperty("resetOn") String resetOn +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderMonthlyUsageScope.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderMonthlyUsageScope.java new file mode 100644 index 0000000000..56d9b4dd38 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderMonthlyUsageScope.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Identity scope of a monthly usage reading. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ProviderMonthlyUsageScope { + /** The {@code user} variant. */ + USER("user"), + /** The {@code unknown} variant. */ + UNKNOWN("unknown"); + + private final String value; + ProviderMonthlyUsageScope(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ProviderMonthlyUsageScope fromValue(String value) { + for (ProviderMonthlyUsageScope v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ProviderMonthlyUsageScope value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderMonthlyUsageState.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderMonthlyUsageState.java new file mode 100644 index 0000000000..7788fbc219 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderMonthlyUsageState.java @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Availability of a service's monthly consumption reading. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ProviderMonthlyUsageState { + /** The {@code available} variant. */ + AVAILABLE("available"), + /** The {@code no_policy} variant. */ + NO_POLICY("no_policy"), + /** The {@code unavailable} variant. */ + UNAVAILABLE("unavailable"), + /** The {@code unknown} variant. */ + UNKNOWN("unknown"); + + private final String value; + ProviderMonthlyUsageState(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ProviderMonthlyUsageState fromValue(String value) { + for (ProviderMonthlyUsageState v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ProviderMonthlyUsageState value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaState.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaState.java index 253d64bbe8..5473712f9c 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaState.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaState.java @@ -49,6 +49,8 @@ public record ProviderQuotaState( @JsonProperty("acquisitionError") String acquisitionError, /** Optional metadata for an authoritative budget. Advisory balances and admission-only observations do not populate this. */ @JsonProperty("budgetMetadata") ProviderQuotaBudgetMetadata budgetMetadata, + /** Service-reported monthly consumption, independent of quota balances and per-call or session cost. Omitted when the service does not report monthly usage. */ + @JsonProperty("monthlyUsage") ProviderMonthlyUsage monthlyUsage, /** Key for the backwards-compatible snapshots projection, when the authoritative budget supports that contract. */ @JsonProperty("compatibilityKey") String compatibilityKey, /** Explicit service admission flag, when reported. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RemediationAction.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RemediationAction.java index 50f6256352..2da0b13624 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RemediationAction.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RemediationAction.java @@ -25,7 +25,9 @@ public enum RemediationAction { /** The {@code review_sandbox_policy} variant. */ REVIEW_SANDBOX_POLICY("review_sandbox_policy"), /** The {@code allow_sandbox_outbound} variant. */ - ALLOW_SANDBOX_OUTBOUND("allow_sandbox_outbound"); + ALLOW_SANDBOX_OUTBOUND("allow_sandbox_outbound"), + /** The {@code rewind_conversation} variant. */ + REWIND_CONVERSATION("rewind_conversation"); private final String value; RemediationAction(String value) { this.value = value; } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RepoSettingsEditParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RepoSettingsEditParams.java new file mode 100644 index 0000000000..c871a3aed7 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RepoSettingsEditParams.java @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * One atomic edit of an allowed top-level repository setting. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record RepoSettingsEditParams( + /** Absolute path of the repository root. */ + @JsonProperty("repoRoot") String repoRoot, + /** Shared or local repository settings file. */ + @JsonProperty("scope") RepoSettingsEditScope scope, + /** One canonical top-level repository setting that the allowlist permits. */ + @JsonProperty("key") String key, + /** Set a scalar, add or remove a string list item, or remove the setting. The handler rejects a missing or incompatible value. */ + @JsonProperty("operation") SettingsEditOperation operation, + /** Required boolean, string or number for set, required string for add-item and remove-item, and forbidden for remove. The runtime rejects all other JSON values. */ + @JsonProperty("value") Object value +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RepoSettingsEditScope.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RepoSettingsEditScope.java new file mode 100644 index 0000000000..43b64573d5 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RepoSettingsEditScope.java @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Which repository settings file to edit. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +enum RepoSettingsEditScope { + /** The {@code repo} variant. */ + REPO("repo"), + /** The {@code local} variant. */ + LOCAL("local"); + + private final String value; + RepoSettingsEditScope(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static RepoSettingsEditScope fromValue(String value) { + for (RepoSettingsEditScope v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown RepoSettingsEditScope value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RepoSettingsReadRawParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RepoSettingsReadRawParams.java new file mode 100644 index 0000000000..40d25ea68c --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RepoSettingsReadRawParams.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Which repository settings file to read. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record RepoSettingsReadRawParams( + /** Absolute path of the repository root. */ + @JsonProperty("repoRoot") String repoRoot, + /** Use repo for shared settings or local for settings.local.json. Other scopes are rejected. */ + @JsonProperty("scope") String scope +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RepoSettingsReadRawResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RepoSettingsReadRawResult.java new file mode 100644 index 0000000000..65d5884033 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RepoSettingsReadRawResult.java @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Parsed repository settings without normalization or layering. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record RepoSettingsReadRawResult( + /** Stored JSON values after the existing JSONC parser and prototype-safety checks. Empty when the file is missing, blank, or invalid. */ + @JsonProperty("config") Object config, + /** True when the file exists but did not parse, so `config` is empty for that reason rather than because nothing is set. */ + @JsonProperty("invalid") Boolean invalid +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RepoSettingsWriteAllParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RepoSettingsWriteAllParams.java new file mode 100644 index 0000000000..8b94ff9be4 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RepoSettingsWriteAllParams.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * The complete set of repository-overridable settings to store. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record RepoSettingsWriteAllParams( + /** Absolute path of the repository root. */ + @JsonProperty("repoRoot") String repoRoot, + /** Use repo for shared settings or local for settings.local.json. Other scopes are rejected. */ + @JsonProperty("scope") String scope, + /** Object of allowed settings to store. Missing allowlisted keys are removed. Other input keys are ignored, while unknown stored keys are preserved. */ + @JsonInclude(JsonInclude.Include.ALWAYS) + @JsonProperty("settings") Object settings +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RepoSettingsWriteKeyParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RepoSettingsWriteKeyParams.java new file mode 100644 index 0000000000..24bc205bc3 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RepoSettingsWriteKeyParams.java @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * One key to write or remove in a repository settings file. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record RepoSettingsWriteKeyParams( + /** Absolute path of the repository root. */ + @JsonProperty("repoRoot") String repoRoot, + /** Use repo for shared settings or local for settings.local.json. Other scopes are rejected. */ + @JsonProperty("scope") String scope, + /** Canonical top-level key permitted by the repository settings allowlist. */ + @JsonProperty("key") String key, + /** Value to validate and write. Required unless delete is true; a null value is validated, not treated as deletion. */ + @JsonProperty("value") Object value, + /** True to remove the key instead of writing it. */ + @JsonProperty("delete") Boolean delete +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxCheckEnforcementCommandParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxCheckEnforcementCommandParams.java new file mode 100644 index 0000000000..b7401231f7 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxCheckEnforcementCommandParams.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * One slash command, as typed, to test against the enforcement-block allowlist. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SandboxCheckEnforcementCommandParams( + /** The command name as the user typed it, with or without its leading slash. */ + @JsonProperty("commandName") String commandName, + /** The command's arguments, in order. Pass an empty array for a bare command: several entries in the allowlist admit the bare form and refuse every argument. */ + @JsonProperty("args") List args +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxCredentialSuggestion.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxCredentialSuggestion.java new file mode 100644 index 0000000000..927dcdf5e9 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxCredentialSuggestion.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * A possible secret-bearing environment variable and optional locally suggested HTTPS injection hosts. This is a draft for user review, not an active grant. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SandboxCredentialSuggestion( + /** Environment variable name. The secret value is never returned. */ + @JsonProperty("name") String name, + /** HTTPS hostnames suggested by a local known-provider mapping. An empty list requires the user to supply injection hosts before adding a masking entry. These suggestions do not allow network access. */ + @JsonProperty("suggestedInjectHosts") List suggestedInjectHosts +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxDocsUrlResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxDocsUrlResult.java new file mode 100644 index 0000000000..bb2ed5c1a2 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxDocsUrlResult.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The documentation URL a host cites when it declines or downgrades a sandbox setting. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SandboxDocsUrlResult( + /** Documentation URL describing the sandbox and the settings that control it. */ + @JsonProperty("url") String url +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxEffectiveForOptionalResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxEffectiveForOptionalResult.java new file mode 100644 index 0000000000..2336d4f195 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxEffectiveForOptionalResult.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The sandbox a named feature actually launches under. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SandboxEffectiveForOptionalResult( + /** The effective sandbox config for the requested feature, or a disabled sandbox when the feature is routed out of it. */ + @JsonProperty("sandboxJson") Object sandboxJson +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxFilterPickerCandidatesParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxFilterPickerCandidatesParams.java new file mode 100644 index 0000000000..663040c87e --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxFilterPickerCandidatesParams.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * A batch of file-picker candidates to check against a prepared policy. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SandboxFilterPickerCandidatesParams( + /** The candidates to check, in the order the answers are returned. */ + @JsonProperty("candidates") List candidates, + /** The session's working directory, matching the one the policy was prepared from. */ + @JsonProperty("cwd") String cwd, + /** The policy returned by `sandbox.preparePickerPolicy`. Omitted keeps every candidate. */ + @JsonProperty("sandbox") Object sandbox +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxFilterPickerCandidatesResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxFilterPickerCandidatesResult.java new file mode 100644 index 0000000000..cbf29a25f3 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxFilterPickerCandidatesResult.java @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Per-candidate allow decisions, positionally aligned with the request. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SandboxFilterPickerCandidatesResult( + /** One decision per candidate, in the same order. Always the same length as the request's `candidates`. */ + @JsonProperty("allowed") List allowed +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxGetUnsupportedStartupNoticeParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxGetUnsupportedStartupNoticeParams.java new file mode 100644 index 0000000000..c082b52e7d --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxGetUnsupportedStartupNoticeParams.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * The managed sandbox policy in force, which selects which remedy the notice offers. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SandboxGetUnsupportedStartupNoticeParams( + /** The managed `sandbox` slice, when a policy supplies one. The notice treats the sandbox as enforced only when this both enables it and sets `failIfUnavailable`, because a managed sandbox that tolerates being unavailable still leaves the user able to turn it off. */ + @JsonProperty("managedSandbox") Object managedSandbox, + /** True when the sandbox stayed on only because the managed policy could not be determined. Distinguishes a policy that positively enforces the sandbox from one that merely failed to resolve, which is the difference between telling the user to contact an administrator and telling them to wait. Defaults to false. */ + @JsonProperty("enabledByUndeterminedPolicy") Boolean enabledByUndeterminedPolicy +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxPickerCandidate.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxPickerCandidate.java new file mode 100644 index 0000000000..f3c7712a82 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxPickerCandidate.java @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * One file-picker suggestion offered up for the sandbox filesystem check. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SandboxPickerCandidate( + /** Absolute path the suggestion would attach. */ + @JsonProperty("path") String path, + /** Whether the suggestion is a directory. A directory the policy does not itself allow may still be needed as a waypoint to an allowed subtree. */ + @JsonProperty("isDirectory") Boolean isDirectory +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxPreparePickerPolicyParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxPreparePickerPolicyParams.java new file mode 100644 index 0000000000..2895a57e73 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxPreparePickerPolicyParams.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * The working directory and sandbox settings to build a file-picker policy from. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SandboxPreparePickerPolicyParams( + /** The session's working directory. Git discovery and grant canonicalization are resolved against it. */ + @JsonProperty("cwd") String cwd, + /** The session's resolved `sandbox` slice. Omitted, or disabled, yields no policy and every candidate is kept. */ + @JsonProperty("sandbox") Object sandbox +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxPreparePickerPolicyResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxPreparePickerPolicyResult.java new file mode 100644 index 0000000000..07cae03587 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxPreparePickerPolicyResult.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The prepared file-picker policy, or none when the sandbox is disabled. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SandboxPreparePickerPolicyResult( + /** The prepared policy to pass back to `sandbox.filterPickerCandidates`. Absent when the sandbox is disabled or absent, in which case filtering keeps every candidate. */ + @JsonProperty("sandboxJson") Object sandboxJson +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaCheckRelevanceParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaCheckRelevanceParams.java new file mode 100644 index 0000000000..6d66db0a30 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaCheckRelevanceParams.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Identifies the credential hosts that the persistent certificate authority of the sandbox credential proxy must cover. The runtime always adds the hosts from the saved user settings. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SandboxProxyCaCheckRelevanceParams( + /** The sandbox configuration that the host gives its sessions. The runtime reads the credential hosts from `auth` and `credentials`; it ignores `enabled` and the other fields. */ + @JsonProperty("sandboxConfig") SandboxConfig sandboxConfig +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxySecretGetResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxySecretGetResult.java new file mode 100644 index 0000000000..5afa3f89d3 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxySecretGetResult.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The stored sandbox proxy password, if one is present. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SandboxProxySecretGetResult( + /** The stored password. Absent when no entry exists in either the keychain or the on-disk fallback store. */ + @JsonProperty("value") String value +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxySecretLocation.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxySecretLocation.java new file mode 100644 index 0000000000..d72162ee2e --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxySecretLocation.java @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Where this runtime keeps the sandbox proxy password. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SandboxProxySecretLocation( + /** Absolute path of the credential store holding the sandbox proxy password. The proxy password belongs to no MCP server, so it is kept in its own directory rather than in the MCP secret store. */ + @JsonProperty("configDir") String configDir, + /** Identifier of the proxy password entry within that store. Derived from the configuration directory, so two Copilot homes on one machine never share an entry. Use it to recognize a `${secret:…}` reference in stored settings as this runtime's own proxy password. */ + @JsonProperty("secretId") String secretId +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxySecretSaveParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxySecretSaveParams.java new file mode 100644 index 0000000000..610fb09681 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxySecretSaveParams.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * The sandbox proxy password to store. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SandboxProxySecretSaveParams( + /** The password to store for the sandbox proxy. */ + @JsonProperty("value") String value +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxySecretSaveResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxySecretSaveResult.java new file mode 100644 index 0000000000..543b38083f --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxySecretSaveResult.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Where the sandbox proxy password was stored. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SandboxProxySecretSaveResult( + /** Whether the OS keychain accepted the value. False means it was written to the on-disk fallback store instead, which is still readable by `sandbox.proxySecret.get`. */ + @JsonProperty("storedInKeychain") Boolean storedInKeychain +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxResolveFlooredConfigParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxResolveFlooredConfigParams.java new file mode 100644 index 0000000000..738642fa05 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxResolveFlooredConfigParams.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * The user's sandbox settings and the managed sandbox floor to raise them to. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SandboxResolveFlooredConfigParams( + /** The user's own `sandbox` settings slice. Omitted when the user has configured none, which still yields the managed floor rather than a disabled sandbox. */ + @JsonProperty("userSandbox") Object userSandbox, + /** The effective managed `sandbox` slice acting as a floor. Omitted when no administrator policy applies, in which case the user's own settings stand unchanged. */ + @JsonProperty("managedSandbox") Object managedSandbox +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxResolveFlooredConfigResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxResolveFlooredConfigResult.java new file mode 100644 index 0000000000..043eaaead9 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxResolveFlooredConfigResult.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The user's sandbox settings raised to the managed floor. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SandboxResolveFlooredConfigResult( + /** The floored sandbox config, normalized through the declared sandbox shape so only declared fields in a fixed order are present. */ + @JsonProperty("sandboxJson") Object sandboxJson +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxResolveForOptionalParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxResolveForOptionalParams.java new file mode 100644 index 0000000000..9a523b274e --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxResolveForOptionalParams.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * A sandbox config and the per-feature routing flag to resolve it for. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SandboxResolveForOptionalParams( + /** The session's resolved `sandbox` slice. Omitted when the host has none, which yields a disabled sandbox rather than an error. */ + @JsonProperty("sandbox") Object sandbox, + /** The routing flag naming the feature being launched, such as `sandboxMcpServers` or `sandboxLspServers`. */ + @JsonProperty("routingFlag") String routingFlag +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxSettingsApplyDefaultsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxSettingsApplyDefaultsParams.java new file mode 100644 index 0000000000..4b129d0d41 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxSettingsApplyDefaultsParams.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * The stored sandbox settings to resolve against the defaults. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SandboxSettingsApplyDefaultsParams( + /** The sandbox settings as stored in the settings file. An empty object resolves to the defaults alone. */ + @JsonInclude(JsonInclude.Include.ALWAYS) + @JsonProperty("stored") Object stored +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxUnsupportedStartupNoticeResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxUnsupportedStartupNoticeResult.java new file mode 100644 index 0000000000..2d31660a33 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxUnsupportedStartupNoticeResult.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The startup warning, absent when the machine can run a sandbox. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SandboxUnsupportedStartupNoticeResult( + /** The user-facing warning, carrying the platform remedy and the recovery the policy in force actually allows. Absent when this machine supports a sandbox, so a host can call unconditionally and print only what it gets. */ + @JsonProperty("notice") String notice +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAccountApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAccountApi.java index 714ef4ebb6..8c25672e33 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAccountApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAccountApi.java @@ -95,4 +95,48 @@ public CompletableFuture logout(AccountLogoutParams params) return caller.invoke("account.logout", params, AccountLogoutResult.class); } + /** + * Renders the sign-in banner and the one-line account description the runtime uses for an authentication identity. The runtime words these in several places for its own callers, so a host that announces a sign-in or an account switch should render them from here rather than composing its own, which would let the two spellings drift apart. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture describeAuthInfo(AccountDescribeAuthInfoParams params) { + return caller.invoke("account.describeAuthInfo", params, AccountDescribeAuthInfoResult.class); + } + + /** + * Reports whether the supplied identity can create gists. Gists are unavailable on GitHub Enterprise Cloud with data residency, and to Enterprise Managed Users on github.com, so a host that offers gist actions should hide or disable them when this reports no support. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture getGistSupport(AccountGetGistSupportParams params) { + return caller.invoke("account.getGistSupport", params, AccountGetGistSupportResult.class); + } + + /** + * Resolves a path on the identity's GitHub host into an absolute URL, so a host can link to settings or documentation on the enterprise instance the user is signed in to rather than to github.com. Falls back to the public host when no identity is supplied. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture getWebUrl(AccountGetWebUrlParams params) { + return caller.invoke("account.getWebUrl", params, AccountGetWebUrlResult.class); + } + + /** + * Names the environment variable supplying a classic personal access token, if one is set. A classic token cannot be upgraded in place, so a host that finds one should tell the user which variable to unset rather than starting an interactive sign-in that the variable would override. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture getTokenEnvVar() { + return caller.invoke("account.getTokenEnvVar", java.util.Map.of(), AccountGetTokenEnvVarResult.class); + } + } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAgentsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAgentsApi.java index ddff436555..287c099c93 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAgentsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAgentsApi.java @@ -8,6 +8,7 @@ package com.github.copilot.generated.rpc; import com.github.copilot.CopilotExperimental; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import javax.annotation.processing.Generated; @@ -37,6 +38,19 @@ public CompletableFuture discover(AgentsDiscoverParams par return caller.invoke("agents.discover", params, AgentsDiscoverResult.class); } + /** + * Discovers custom agents across user, project, plugin, and remote sources. + *

+ * Accepts the extensible request, including inputs added after the params record. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture discover(AgentsDiscoverRequest request) { + return caller.invoke("agents.discover", Objects.requireNonNull(request, "request"), AgentsDiscoverResult.class); + } + /** * 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. * @@ -48,6 +62,19 @@ public CompletableFuture getDiscoveryPaths(Agents return caller.invoke("agents.getDiscoveryPaths", params, AgentsGetDiscoveryPathsResult.class); } + /** + * 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. + *

+ * Accepts the extensible request, including inputs added after the params record. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture getDiscoveryPaths(AgentsGetDiscoveryPathsRequest request) { + return caller.invoke("agents.getDiscoveryPaths", Objects.requireNonNull(request, "request"), AgentsGetDiscoveryPathsResult.class); + } + /** * Lists the agents this runtime ships, by name. A consumer separating shipped agents from ones the user or a plugin authored should compare against these names rather than against `AgentInfo.source`: an authored agent may carry the `builtin` source while not being one of these, and the runtime treats the two as separate questions. `disableableNames` is the subset a user may turn off, which a client needs to decide whether to offer a toggle. `yamlBasedNames` is the subset backed by a shipped YAML definition, which a client needs before asking the runtime to load one. * diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerFeatureFlagsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerFeatureFlagsApi.java new file mode 100644 index 0000000000..b2f3444b0a --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerFeatureFlagsApi.java @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import javax.annotation.processing.Generated; + +/** + * API methods for the {@code featureFlags} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +final class ServerFeatureFlagsApi { + + private final RpcCaller caller; + + /** @param caller the RPC transport function */ + ServerFeatureFlagsApi(RpcCaller caller) { + this.caller = caller; + } + + /** + * Resolves named gates against this installation's cached online-experiment assignment, for the account the runtime is already authenticated as. Unlike `featureFlags.resolve`, which layers static availability with the populations supplied, this reads the experiment assignment the runtime cached for its own current authentication, so a launch can honor a rollout before any session exists. The account is never taken from the request: naming one would read another account's cached assignments. The cached assignment answers immediately and any network refresh happens behind it, so this call does not wait on the network. Not available to an extension, which cannot carry the user's consent to read their assignment. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture> resolveStartupGates(FeatureFlagsResolveStartupGatesParams params) { + return caller.invoke("featureFlags.resolveStartupGates", params, RpcMapper.INSTANCE.getTypeFactory().constructMapType(Map.class, String.class, Boolean.class)); + } + + /** + * Lists every feature flag this runtime declares, with the population each one is declared for, its experimental description, and the online-experiment flag it maps to. This is the static catalog: it describes how each flag is defined, not whether it is on for anyone. Use `featureFlags.resolve` for static values with caller overrides, not active runtime values or experiment assignments. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture listMetadata() { + return caller.invoke("featureFlags.listMetadata", java.util.Map.of(), FeatureFlagsMetadataResult.class); + } + + /** + * Computes static feature flags from the supplied population and overrides without changing runtime state or verifying audience membership. Starts from declared availability, then applies direct feature-flag environment values, user settings, and explicit `flagOverrides`. It does not apply `COPILOT_EXP_*` experiment assignments, authenticated startup gates, or active runtime state. Settings include `enabledFeatureFlags`, the legacy `feature_flags.enabled` list, and supported convenience settings. Supply `baseFlags` to use an existing flag set instead of declared availability. Omit `isStaffGitHub` or `isStaffMicrosoft` when membership was never recorded: a legacy staff account then counts as both populations. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture> resolve(FeatureFlagsResolveParams params) { + return caller.invoke("featureFlags.resolve", params, RpcMapper.INSTANCE.getTypeFactory().constructMapType(Map.class, String.class, Boolean.class)); + } + +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerGlobalStateApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerGlobalStateApi.java index 3c215395b8..2d41182ae0 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerGlobalStateApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerGlobalStateApi.java @@ -49,7 +49,7 @@ CompletableFuture loadForConfigDir(GlobalStateLoadForConf } /** - * Records one top-level key in the host's machine-wide state, the counterpart to `globalState.load`. A host calls this to remember that it has shown an onboarding step, asked a one-off question, or completed a migration, so the next run can skip it. Only the named key is replaced and the rest of the document is preserved, which lets two writers record different flags without overwriting each other; passing no value removes the key instead. Only the keys a host records itself are writable: `appInstallNudgeResponded`, `appTipShown`, `askedSetupTerminals`, `autoFeedbackLastPromptedAt`, `firstLaunchAt`, `recentModelIds`, `sandboxCredentialProxyCaDeclined` and `sandboxOnboardingShown`. Every other key is refused, including `installedPlugins`, the stored credentials, `trustedFolders`, the staff flags and the signed-in accounts. Plugin enablement must use the plugin APIs, which apply repository and managed-policy checks. + * Records one top-level key in the host's machine-wide state, the counterpart to `globalState.load`. A host calls this to remember that it has shown an onboarding step, asked a one-off question, or completed a migration, so the next run can skip it. Only the named key is replaced and the rest of the document is preserved, which lets two writers record different flags without overwriting each other; passing no value removes the key instead. Only the keys a host records itself are writable: `appTipShown`, `askedSetupTerminals`, `autoFeedbackLastPromptedAt`, `firstLaunchAt`, `recentModelIds`, `sandboxCredentialProxyCaDeclined` and `sandboxOnboardingShown`. Every other key is refused, including `installedPlugins`, the stored credentials, `trustedFolders`, the staff flags and the signed-in accounts. Plugin enablement must use the plugin APIs, which apply repository and managed-policy checks. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerHooksApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerHooksApi.java index f41fbd63ac..0997c6588f 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerHooksApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerHooksApi.java @@ -8,6 +8,7 @@ package com.github.copilot.generated.rpc; import com.github.copilot.CopilotExperimental; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import javax.annotation.processing.Generated; @@ -37,4 +38,17 @@ public CompletableFuture discover(HooksDiscoverParams param return caller.invoke("hooks.discover", params, HooksDiscoverResult.class); } + /** + * Discovers hook actions enabled under server-side discovery settings from user, repository, plugin, and managed-policy sources. + *

+ * Accepts the extensible request, including inputs added after the params record. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture discover(HooksDiscoverRequest request) { + return caller.invoke("hooks.discover", Objects.requireNonNull(request, "request"), HooksDiscoverResult.class); + } + } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerInstructionSourceList.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerInstructionSourceList.java new file mode 100644 index 0000000000..cddf0f9f88 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerInstructionSourceList.java @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Instruction sources discovered across user, repository, and plugin sources. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ServerInstructionSourceList( + /** All discovered instruction sources */ + @JsonProperty("sources") List sources +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerInstructionsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerInstructionsApi.java index 23d8c7e2de..55298f8c4a 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerInstructionsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerInstructionsApi.java @@ -8,6 +8,7 @@ package com.github.copilot.generated.rpc; import com.github.copilot.CopilotExperimental; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import javax.annotation.processing.Generated; @@ -26,6 +27,17 @@ public final class ServerInstructionsApi { this.caller = caller; } + /** + * Returns the prompt the `init` command runs to author repository instructions. A host offering its own initialize action sends this text so the instructions it produces match the ones the CLI produces. The text is fixed for a build and depends on no configuration, session or repository. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture getInitPrompt() { + return caller.invoke("instructions.getInitPrompt", java.util.Map.of(), InstructionsInitPromptResult.class); + } + /** * Discovers instruction sources across user, repository, and plugin sources. * @@ -37,6 +49,30 @@ public CompletableFuture discover(InstructionsDiscov return caller.invoke("instructions.discover", params, InstructionsDiscoverResult.class); } + /** + * Discovers instruction sources across user, repository, and plugin sources. + *

+ * Accepts the extensible request, including inputs added after the params record. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture discover(InstructionsDiscoverRequest request) { + return caller.invoke("instructions.discover", Objects.requireNonNull(request, "request"), InstructionsDiscoverResult.class); + } + + /** + * Discovers the instruction sources for one caller-named location and returns them in the order the runtime applies them. `discover` answers for a set of project paths and derives the rest of its scope itself; this one instead takes every argument from the caller — the extra directories to scan and whether to descend into nested instruction files — and honors them as given. It also includes instruction directories from trusted repository settings, matching native repository discovery. Use it when the caller knows its own scope and no session exists to derive that scope from, such as a host reporting how many instruction files a directory contributes before any session starts. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract; it is migration plumbing, not a surface consumers are meant to depend on. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture discoverRepoSources(InstructionsDiscoverRepoSourcesParams params) { + return caller.invoke("instructions.discoverRepoSources", params, ServerInstructionSourceList.class); + } + /** * 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. * @@ -48,4 +84,28 @@ public CompletableFuture getDiscoveryPaths( return caller.invoke("instructions.getDiscoveryPaths", params, InstructionsGetDiscoveryPathsResult.class); } + /** + * 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. + *

+ * Accepts the extensible request, including inputs added after the params record. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture getDiscoveryPaths(InstructionsGetDiscoveryPathsRequest request) { + return caller.invoke("instructions.getDiscoveryPaths", Objects.requireNonNull(request, "request"), InstructionsGetDiscoveryPathsResult.class); + } + + /** + * Drops the runtime's in-memory cache of discovered custom instructions so the next discovery reads them from disk again. A host calls this after it changes instruction files, or after it trusts a folder whose instructions an earlier discovery skipped, so that a session started afterwards sees the current files instead of a cached earlier answer. The call performs no I/O of its own and removes nothing from disk; it only invalidates, so the cost lands on the next discovery. Returns once the cache is clear, which is what lets a caller order a session start after it. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract; it is migration plumbing, not a surface consumers are meant to depend on. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture clearCache() { + return caller.invoke("instructions.clearCache", java.util.Map.of(), Void.class); + } + } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerManagedSettingsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerManagedSettingsApi.java index a686df708c..666a1052e0 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerManagedSettingsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerManagedSettingsApi.java @@ -21,9 +21,123 @@ public final class ServerManagedSettingsApi { private final RpcCaller caller; + /** API methods for the {@code managedSettings.permissions} sub-namespace. */ + public final ServerManagedSettingsPermissionsApi permissions; + /** @param caller the RPC transport function */ ServerManagedSettingsApi(RpcCaller caller) { this.caller = caller; + this.permissions = new ServerManagedSettingsPermissionsApi(caller); + } + + /** + * Discovers device-managed settings the way the runtime's own device layer does, and reports the sandbox floor and the load outcome alongside the validated document. This differs from `managedSettings.read` in three ways a policy-enforcing host depends on: a document that validates to an empty object is reported as absent rather than as an empty policy, a discovery error does not suppress a document that still validates, and a failure is reported as a flag rather than only as error text. Hosts that compose the device layer into an effective policy use this, because `managedSettings.read` cannot distinguish "no device policy" from "device policy could not be read" and would silently hand the user's own layer the win. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture readDevice() { + return caller.invoke("managedSettings.readDevice", java.util.Map.of(), ManagedSettingsReadDeviceResult.class); + } + + /** + * Retains the supplied managed layers as this runtime process's startup policy and re-applies the result to every live session. Hosts that own their own short-lived process policy use this, so a guarded mutation is checked against the policy the host actually fetched. Retaining is a whole-process replacement rather than a merge, so a host that already resolved a good policy must not retain a partial one over it. The failure flags are honest inputs rather than assumptions: reporting a fetch or load that failed keeps the layers already retained, and the interim fail-closed state, instead of clearing them. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture retainPolicy(ManagedSettingsRetainPolicyParams params) { + return caller.invoke("managedSettings.retainPolicy", params, Void.class); + } + + /** + * Stamps an effective managed policy's individually-controlled entries onto a user settings document, returning the settings a host should actually act on. The managed layer wins on every plugin spec and marketplace name it names, and leaves everything it does not name as the user wrote it. Hosts use this rather than merging the two documents themselves, because the merge is per entry rather than per document and a whole-document overlay would either discard the user's unmanaged entries or let a user entry outrank a managed one. Omitting the managed layer returns the settings unchanged, which is what an unmanaged host wants. Rejects when either document is present but is not a JSON object rather than treating it as absent, since silently dropping a malformed policy hands the user's layer the win on exactly the entries the policy exists to control. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture applyControls(ManagedSettingsApplyControlsParams params) { + return caller.invoke("managedSettings.applyControls", params, ManagedSettingsApplyControlsResult.class); + } + + /** + * Validates the managed settings a client is supplying for a session it is about to open, and returns them reduced to the keys a client is allowed to set. A client-supplied policy is not the same thing as an administrator's: it may only narrow permissions, so this accepts `permissions` and rejects every other key outright rather than ignoring it. Rejecting is the point — a host that silently dropped an unsupported key would report a policy as applied while running without it. Hosts call this before opening the session rather than letting the open fail, so a malformed policy is reported against the request that carried it. The runtime applies the same rule again when the session starts, so this cannot be used to smuggle a wider document past it. An accepted document that sets nothing comes back as an empty object, which is distinct from supplying nothing at all. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture parseClientValue(ManagedSettingsParseClientValueParams params) { + return caller.invoke("managedSettings.parseClientValue", params, ManagedSettingsParseClientValueResult.class); + } + + /** + * Composes the supplied server, device/MDM, device sandbox floor and policy-helper layers into the single effective managed policy a host should enforce, resolving each key across the layers by the runtime's own precedence rules. Hosts that assemble managed layers themselves use this so their precedence matches the runtime's exactly instead of reimplementing it. The composition is not a pure function of its inputs: an advisory sandbox force-on that arrived over the server channel is declined when this host cannot run a sandbox, so the result reflects the runtime host's capabilities, and the returned settings carry `sandboxAdvisoryFloorDeclined: true` when that happened. Enforced channels (device/MDM and policy helper) are never declined, and a server force-on carrying `failIfUnavailable` is preserved so the session fails loudly instead of quietly running unsandboxed. Rejects when a supplied layer is present but is not a JSON object, rather than treating it as unmanaged, because silently dropping a malformed policy would hand the user's own layer the win on exactly the keys the policy exists to control. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture resolveEffectiveValue(ManagedSettingsResolveEffectiveValueParams params) { + return caller.invoke("managedSettings.resolveEffectiveValue", params, ManagedSettingsResolveEffectiveValueResult.class); + } + + /** + * Decides what a host must do to its bypass-permissions state after a managed-policy resolution lands, given the state already in force and the account now signed in. A host holds two pieces of state — which layer disabled bypass mode, and for a server-delivered disable, the account it was pinned to — and cannot decide the next step from the new policy alone. Three rules make that so, and each one is a control rather than a preference. A device/MDM disable outranks the account, so a later server answer never lifts it. An unknown policy, which is what a failed fetch or a refusal this build cannot enforce yields, preserves whatever is in force instead of reverting to permissive. And a server disable is lifted only when the account it was pinned to is no longer the signed-in one, which is what makes signing in to an unrestricted account recover without a restart while a reconnect on the same restricted account does not. Hosts call this rather than reimplementing it because getting the pinning backwards either strands a disable on an account that never carried one or drops one that still applies. `setDisabledState` is omitted when the host must leave its stored flag untouched, which is distinct from being told to set it to false. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture resolveBypassDisableTransition(ManagedSettingsResolveBypassDisableTransitionParams params) { + return caller.invoke("managedSettings.resolveBypassDisableTransition", params, ManagedSettingsResolveBypassDisableTransitionResult.class); + } + + /** + * Resolves managed (enterprise policy) settings for a host from every layer at once: the account's server-delivered policy, the device/MDM layer, and a platform policy helper. Hosts use this instead of fetching and composing the layers themselves, because the per-layer precedence and the fail-closed rules that apply when a layer cannot be confirmed are the runtime's own and must match the policy a session will later enforce. It does not retain or apply anything to process-global policy state, so a host may call it before any session exists — which is the point, since plugin activation and one-shot commands resolve policy before a session is available to carry it. The returned per-layer outcome flags are load-bearing rather than diagnostic: a host must distinguish an unmanaged device from a managed device whose policy could not be confirmed, and apply its fail-closed gates in the second case. Reports its diagnostics in `logMessages` rather than logging them, so a host that runs before a log sink exists still sees them. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture ingest(ManagedSettingsIngestParams params) { + return caller.invoke("managedSettings.ingest", params, ManagedSettingsIngestResult.class); + } + + /** + * Resolves the managed (enterprise policy) settings a host must enforce from startup, and judges the account against the enterprise sign-in pin in the same pass. This composes the layers the way `managedSettings.ingest` does and then answers the two questions a host cannot answer for itself: which fail-closed floors apply when a layer could not be confirmed, and whether this credential is allowed to sign in at all. The sign-in verdict is the reason a host must not assemble this itself from `managedSettings.ingest` — a pinned organization is an access control, and a host that composed the layers but skipped the pin would let a developer sign in with a personal or another enterprise's account and escape every control that keys off the signed-in identity. The verdict is computed on every resolution, including one that is not retained, so declining to apply policy never widens access. Set `retainAndApply` to keep the resolved policy as this runtime process's startup policy; leave it false to resolve without side effects. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture resolveStartupPolicy(ManagedSettingsResolveStartupPolicyParams params) { + return caller.invoke("managedSettings.resolveStartupPolicy", params, ManagedSettingsResolveStartupPolicyResult.class); + } + + /** + * Resolves the managed `remoteControl` policy for an account on its own, for a host deciding whether a remote-control session may start. Composes the same layers as `managedSettings.ingest` and returns only that one key, so a host does not have to know which layer won. An absent policy means the account is unmanaged for this capability and the host may proceed; that default is safe only while absence really does mean "no policy", so a service refusal that says an account policy exists which this client is too old to enforce resolves to an explicit disabled policy instead of to absence. A resolution that cannot be completed is rejected rather than answered as absence, for the same reason. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture loadRemoteControl(ManagedSettingsLoadRemoteControlParams params) { + return caller.invoke("managedSettings.loadRemoteControl", params, ManagedSettingsLoadRemoteControlResult.class); + } + + /** + * Evaluates the device-channel enterprise sign-in pin (MDM plist, registry, or managed-settings file) against a credential, without resolving any server-delivered policy. This is the gate an entry point runs before it branches, and the only one that can refuse a developer's first sign-in: it is local, needs no credential, and a server-delivered pin only ever reaches accounts already inside the organization. It fails closed by construction — a discovery that cannot determine whether a pin exists refuses rather than allows, because a control that fails open under a triggerable condition is not a control. Hosts that also want the server channel should call `managedSettings.resolveStartupPolicy`, which folds both channels into one verdict. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture resolveDeviceForceLoginOrgsGate(ManagedSettingsResolveDeviceForceLoginOrgsGateParams params) { + return caller.invoke("managedSettings.resolveDeviceForceLoginOrgsGate", params, ForceLoginOrgsDecision.class); } /** diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerManagedSettingsPermissionsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerManagedSettingsPermissionsApi.java new file mode 100644 index 0000000000..11526bde65 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerManagedSettingsPermissionsApi.java @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import java.util.concurrent.CompletableFuture; +import javax.annotation.processing.Generated; + +/** + * API methods for the {@code managedSettings.permissions} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class ServerManagedSettingsPermissionsApi { + + private final RpcCaller caller; + + /** @param caller the RPC transport function */ + ServerManagedSettingsPermissionsApi(RpcCaller caller) { + this.caller = caller; + } + + /** + * Pure, sessionless evaluation of operations against a supplied permissionsContext. Performs no policy discovery, I/O, prompting, navigation, permission grants, or sandbox changes. Obtain the context from managedSettings.resolve or session.managedSettings.get, or reuse an in-memory context. Results preserve input order and duplicates. Consumers must block deny, obtain approval for ask, and use their normal permission flow for unmanaged. Unknown policy returns failClosed with every operation denied. This is not a substitute for user permissions or other security controls. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture evaluate(ManagedSettingsPermissionsEvaluateParams params) { + return caller.invoke("managedSettings.permissions.evaluate", params, ManagedSettingsPermissionsEvaluateResult.class); + } + +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerMcpApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerMcpApi.java index 553a644214..c35de551fc 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerMcpApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerMcpApi.java @@ -24,6 +24,8 @@ public final class ServerMcpApi { /** API methods for the {@code mcp.config} sub-namespace. */ public final ServerMcpConfigApi config; + /** API methods for the {@code mcp.registry} sub-namespace. */ + final ServerMcpRegistryApi registry; /** API methods for the {@code mcp.installations} sub-namespace. */ public final ServerMcpInstallationsApi installations; @@ -31,6 +33,7 @@ public final class ServerMcpApi { ServerMcpApi(RpcCaller caller) { this.caller = caller; this.config = new ServerMcpConfigApi(caller); + this.registry = new ServerMcpRegistryApi(caller); this.installations = new ServerMcpInstallationsApi(caller); } @@ -45,6 +48,28 @@ public CompletableFuture discover(McpDiscoverParams params) { return caller.invoke("mcp.discover", params, McpDiscoverResult.class); } + /** + * Lists the MCP servers the runtime supplies itself, so a host that disables the built-ins wholesale suppresses exactly the set the runtime would otherwise add. The names are fixed for a build and depend on no configuration, session or account. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture listBuiltinServerNames() { + return caller.invoke("mcp.listBuiltinServerNames", java.util.Map.of(), McpListBuiltinServerNamesResult.class); + } + + /** + * Reports whether the gh-replaceable GitHub MCP tools may be clipped for a session, which a host feeds back as the `excludeGhReplaceableTools` build option. The tools are redundant only when the session can reach a shell *and* the host has the `gh` that would replace them, so both halves are decided here. The shell half runs the platform shell tool through the runtime's own tool-filter matcher, so entry forms like `builtin:bash` and `builtin:*` behave exactly as they do when a session builds its tool catalog. The host half probes for `gh`, and is skipped entirely when the filters already rule the shell out. Host presence alone is not enough: a session restricted to, say, `view` would otherwise lose the built-in issue-read fallback while having no shell to replace it with. The answer describes the host the runtime runs on, so it is never forwarded to a remote engine. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture shouldExcludeGitHubTools(McpShouldExcludeGitHubToolsParams params) { + return caller.invoke("mcp.shouldExcludeGitHubTools", params, McpShouldExcludeGitHubToolsResult.class); + } + /** * Requests a side-effect-free MCP install plan from a catalog candidate handle or a caller-supplied card. This host-implemented server method is available through SDK/TUI hosts; standalone and C-ABI runtimes whose host does not implement server-method dispatch return JSON-RPC MethodNotFound. A runtime with planning available returns a normalised plan and opaque single-use plan handle; a runtime without it returns the typed planning-unavailable result. A completed plan reports resource identity, provenance, eligible transport choices, the user-scope target, required typed values and secret placeholders, the policy result, the configuration changes installing would make, and whether a reload would be needed. Planning never writes configuration, stores a secret, or reloads MCP servers, so abandoning a plan needs no call and leaves nothing behind. * diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerMcpRegistryApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerMcpRegistryApi.java new file mode 100644 index 0000000000..26b7b2cc7f --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerMcpRegistryApi.java @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import java.util.concurrent.CompletableFuture; +import javax.annotation.processing.Generated; + +/** + * API methods for the {@code mcp.registry} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +final class ServerMcpRegistryApi { + + private final RpcCaller caller; + + /** @param caller the RPC transport function */ + ServerMcpRegistryApi(RpcCaller caller) { + this.caller = caller; + } + + /** + * Allocates an ID for one cancellable MCP registry search. The ID exists before the search starts, so callers can cancel before it starts. The networking stack supplies the cancellation namespace. The runtime retains at most 1,024 unused IDs. At capacity, another allocation can reclaim an unused ID. Active searches retain their IDs until they finish. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture allocateRequestId() { + return caller.invoke("mcp.registry.allocateRequestId", java.util.Map.of(), McpRegistryRequestIdResult.class); + } + + /** + * Searches the MCP registry the supplied credential may read, resolving the registry endpoint from policy first. Hosts usually send credential-free `AuthIdentity`; a `token` identity can be resolved only when it embeds a token, while `env` and `gh-cli` identities can use a token embedded in the request first. The runtime follows registry pages, keeps the newest entry per server name, cuts the list to `limit`, and sorts an empty-query result by GitHub stars. Each server object is carried opaquely. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture search(McpRegistrySearchParams params) { + return caller.invoke("mcp.registry.search", params, McpRegistrySearchResult.class); + } + + /** + * Abandons the registry search that uses the given request ID. It acts only on IDs from `mcp.registry.allocateRequestId`, so it never cancels another component's request. Answers `canceled: true` when it stops a running search. Answers `canceled: false` for unknown or reclaimed IDs, completed or canceled searches, and unused reservations. It releases an unused reservation, so a later search with that ID is refused. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture cancel(McpRegistryCancelParams params) { + return caller.invoke("mcp.registry.cancel", params, McpRegistryCancelResult.class); + } + +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPermissionsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPermissionsApi.java new file mode 100644 index 0000000000..7e1c94d468 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPermissionsApi.java @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import java.util.concurrent.CompletableFuture; +import javax.annotation.processing.Generated; + +/** + * API methods for the {@code permissions} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +final class ServerPermissionsApi { + + private final RpcCaller caller; + + /** API methods for the {@code permissions.locations} sub-namespace. */ + final ServerPermissionsLocationsApi locations; + /** API methods for the {@code permissions.folderTrust} sub-namespace. */ + final ServerPermissionsFolderTrustApi folderTrust; + + /** @param caller the RPC transport function */ + ServerPermissionsApi(RpcCaller caller) { + this.caller = caller; + this.locations = new ServerPermissionsLocationsApi(caller); + this.folderTrust = new ServerPermissionsFolderTrustApi(caller); + } + + /** + * Interprets a verbatim managed `permissions` slice into the three policy questions a host asks before granting elevated permissions: whether bypass-permissions mode is forbidden, whether a policy exists that this build could not determine, and whether the layer supplies permission rules the session must enforce. An absent or empty slice means no managed policy applies. The runtime and its permission service share one policy type, so a new managed permission setting is interpreted in exactly one place; a host that reimplements the reading would silently diverge the first time that set grows. Hosts must fail closed on an undetermined policy rather than treating it as permission granted. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture evaluateManagedPolicy(PermissionsEvaluateManagedPolicyParams params) { + return caller.invoke("permissions.evaluateManagedPolicy", params, PermissionsEvaluateManagedPolicyResult.class); + } + +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPermissionsFolderTrustApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPermissionsFolderTrustApi.java new file mode 100644 index 0000000000..f27b5f0ed1 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPermissionsFolderTrustApi.java @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import java.util.concurrent.CompletableFuture; +import javax.annotation.processing.Generated; + +/** + * API methods for the {@code permissions.folderTrust} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +final class ServerPermissionsFolderTrustApi { + + private final RpcCaller caller; + + /** @param caller the RPC transport function */ + ServerPermissionsFolderTrustApi(RpcCaller caller) { + this.caller = caller; + } + + /** + * Reports whether a folder is trusted, reading the persisted trusted-folders list directly rather than through a session. Use this from a host that has no session yet, such as a one-shot command deciding whether to honor a checkout's own settings. It differs from the session-scoped check in two ways that matter: it takes the configuration directory to read the list from, instead of inheriting the session's, and it answers only from the persisted list, so trust a user granted for the lifetime of one session is not visible here. A host that has a session should prefer the session-scoped check, which also covers session-only trust. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture checkTrust(PermissionsFolderTrustCheckTrustParams params) { + return caller.invoke("permissions.folderTrust.checkTrust", params, FolderTrustCheckResult.class); + } + +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPermissionsLocationsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPermissionsLocationsApi.java new file mode 100644 index 0000000000..7b23a73b3f --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPermissionsLocationsApi.java @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import java.util.concurrent.CompletableFuture; +import javax.annotation.processing.Generated; + +/** + * API methods for the {@code permissions.locations} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +final class ServerPermissionsLocationsApi { + + private final RpcCaller caller; + + /** @param caller the RPC transport function */ + ServerPermissionsLocationsApi(RpcCaller caller) { + this.caller = caller; + } + + /** + * Drops stored per-location permission grants whose directory no longer exists, and reports which location keys were removed. Runs against the permission store under the runtime's own Copilot home, the same store `session.permissions.locations.resolve` reads. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract; it is migration plumbing, not a surface consumers are meant to depend on. It can become public once an SDK consumer needs to prune its own permission store. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture cleanupOrphaned() { + return caller.invoke("permissions.locations.cleanupOrphaned", java.util.Map.of(), LocationPermissionsCleanupResult.class); + } + +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPersistenceApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPersistenceApi.java new file mode 100644 index 0000000000..a98b036d8a --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPersistenceApi.java @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import java.util.concurrent.CompletableFuture; +import javax.annotation.processing.Generated; + +/** + * API methods for the {@code persistence} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +final class ServerPersistenceApi { + + private final RpcCaller caller; + + /** @param caller the RPC transport function */ + ServerPersistenceApi(RpcCaller caller) { + this.caller = caller; + } + + /** + * Atomically replaces a JSON file's entire contents while holding the runtime's per-path lock. The lock lives in the runtime process, so a host that writes these files itself takes no part in it and can tear a concurrent runtime write; route the write here instead. Unlike a merging write this never reads or parses the existing file, so a previously corrupted snapshot is replaced wholesale rather than failing every later write. Use it only for callers that always persist a complete snapshot. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture writeRawJsonLocked(PersistenceWriteRawJsonLockedParams params) { + return caller.invoke("persistence.writeRawJsonLocked", params, PersistenceWriteRawJsonLockedResult.class); + } + +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerRepoApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerRepoApi.java new file mode 100644 index 0000000000..3db04fe14a --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerRepoApi.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import java.util.concurrent.CompletableFuture; +import javax.annotation.processing.Generated; + +/** + * API methods for the {@code repo} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +final class ServerRepoApi { + + private final RpcCaller caller; + + /** API methods for the {@code repo.settings} sub-namespace. */ + final ServerRepoSettingsApi settings; + + /** @param caller the RPC transport function */ + ServerRepoApi(RpcCaller caller) { + this.caller = caller; + this.settings = new ServerRepoSettingsApi(caller); + } + +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerRepoSettingsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerRepoSettingsApi.java new file mode 100644 index 0000000000..247545adbc --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerRepoSettingsApi.java @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import java.util.concurrent.CompletableFuture; +import javax.annotation.processing.Generated; + +/** + * API methods for the {@code repo.settings} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +final class ServerRepoSettingsApi { + + private final RpcCaller caller; + + /** @param caller the RPC transport function */ + ServerRepoSettingsApi(RpcCaller caller) { + this.caller = caller; + } + + /** + * Atomically edits one allowed top-level repository setting. Reads the raw file, preserves unrelated keys, applies one scalar or list edit, validates the result and writes under one file lock, so concurrent sibling or list edits are not lost. A malformed or unsafe existing file returns `invalid` without a write; rejected keys, invalid values and I/O errors reject the call. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture edit(RepoSettingsEditParams params) { + return caller.invoke("repo.settings.edit", params, SettingsEditResult.class); + } + + /** + * Reads one repository settings file without settings normalization or layering. The existing JSONC parser accepts comments and trailing commas and applies its prototype-safety rules. Missing or blank files return an empty valid config. Invalid files return an empty config with invalid=true; other read failures reject. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture readRaw(RepoSettingsReadRawParams params) { + return caller.invoke("repo.settings.readRaw", params, RepoSettingsReadRawResult.class); + } + + /** + * Writes or removes one allowed top-level repository setting through the existing validator and file lock. Preserves unrelated stored keys. Invalid keys, values, existing documents, and I/O errors reject without replacing the file. Deletion requires delete=true; otherwise value must be present. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture writeKey(RepoSettingsWriteKeyParams params) { + return caller.invoke("repo.settings.writeKey", params, Void.class); + } + + /** + * Replaces the repository-overridable settings in one locked write. Allowed keys present in settings are validated and written; allowed keys absent from settings are removed. Other input keys are ignored, and unknown keys already stored in the file remain unchanged. Invalid values or an invalid existing file reject without a partial write. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture writeAll(RepoSettingsWriteAllParams params) { + return caller.invoke("repo.settings.writeAll", params, Void.class); + } + +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java index 99f65a5f89..51b83f9864 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java @@ -25,6 +25,20 @@ public final class ServerRpc { private final RpcCaller caller; + /** API methods for the {@code sandbox} namespace. */ + public final ServerSandboxApi sandbox; + /** API methods for the {@code persistence} namespace. */ + final ServerPersistenceApi persistence; + /** API methods for the {@code user} namespace. */ + public final ServerUserApi user; + /** API methods for the {@code repo} namespace. */ + final ServerRepoApi repo; + /** API methods for the {@code featureFlags} namespace. */ + final ServerFeatureFlagsApi featureFlags; + /** API methods for the {@code permissions} namespace. */ + final ServerPermissionsApi permissions; + /** API methods for the {@code managedSettings} namespace. */ + public final ServerManagedSettingsApi managedSettings; /** API methods for the {@code environments} namespace. */ public final ServerEnvironmentsApi environments; /** API methods for the {@code host} namespace. */ @@ -33,8 +47,6 @@ public final class ServerRpc { public final ServerHooksApi hooks; /** API methods for the {@code models} namespace. */ public final ServerModelsApi models; - /** API methods for the {@code sandbox} namespace. */ - public final ServerSandboxApi sandbox; /** API methods for the {@code tools} namespace. */ public final ServerToolsApi tools; /** API methods for the {@code account} namespace. */ @@ -45,6 +57,8 @@ public final class ServerRpc { public final ServerMcpApi mcp; /** API methods for the {@code extensions} namespace. */ public final ServerExtensionsApi extensions; + /** API methods for the {@code instructions} namespace. */ + public final ServerInstructionsApi instructions; /** API methods for the {@code skills} namespace. */ public final ServerSkillsApi skills; /** API methods for the {@code catalog} namespace. */ @@ -53,22 +67,16 @@ public final class ServerRpc { public final ServerPluginsApi plugins; /** API methods for the {@code agents} namespace. */ public final ServerAgentsApi agents; - /** API methods for the {@code instructions} namespace. */ - public final ServerInstructionsApi instructions; /** API methods for the {@code globalState} namespace. */ final ServerGlobalStateApi globalState; /** API methods for the {@code commands} namespace. */ public final ServerCommandsApi commands; - /** API methods for the {@code user} namespace. */ - public final ServerUserApi user; /** API methods for the {@code gitHubRepository} namespace. */ final ServerGitHubRepositoryApi gitHubRepository; /** API methods for the {@code gitHubOwners} namespace. */ final ServerGitHubOwnersApi gitHubOwners; /** API methods for the {@code git} namespace. */ final ServerGitApi git; - /** API methods for the {@code managedSettings} namespace. */ - public final ServerManagedSettingsApi managedSettings; /** API methods for the {@code runtime} namespace. */ public final ServerRuntimeApi runtime; /** API methods for the {@code sessionFs} namespace. */ @@ -89,28 +97,32 @@ public final class ServerRpc { */ public ServerRpc(RpcCaller caller) { this.caller = caller; + this.sandbox = new ServerSandboxApi(caller); + this.persistence = new ServerPersistenceApi(caller); + this.user = new ServerUserApi(caller); + this.repo = new ServerRepoApi(caller); + this.featureFlags = new ServerFeatureFlagsApi(caller); + this.permissions = new ServerPermissionsApi(caller); + this.managedSettings = new ServerManagedSettingsApi(caller); this.environments = new ServerEnvironmentsApi(caller); this.host = new ServerHostApi(caller); this.hooks = new ServerHooksApi(caller); this.models = new ServerModelsApi(caller); - this.sandbox = new ServerSandboxApi(caller); this.tools = new ServerToolsApi(caller); this.account = new ServerAccountApi(caller); this.secrets = new ServerSecretsApi(caller); this.mcp = new ServerMcpApi(caller); this.extensions = new ServerExtensionsApi(caller); + this.instructions = new ServerInstructionsApi(caller); this.skills = new ServerSkillsApi(caller); this.catalog = new ServerCatalogApi(caller); this.plugins = new ServerPluginsApi(caller); this.agents = new ServerAgentsApi(caller); - this.instructions = new ServerInstructionsApi(caller); this.globalState = new ServerGlobalStateApi(caller); this.commands = new ServerCommandsApi(caller); - this.user = new ServerUserApi(caller); this.gitHubRepository = new ServerGitHubRepositoryApi(caller); this.gitHubOwners = new ServerGitHubOwnersApi(caller); this.git = new ServerGitApi(caller); - this.managedSettings = new ServerManagedSettingsApi(caller); this.runtime = new ServerRuntimeApi(caller); this.sessionFs = new ServerSessionFsApi(caller); this.llmInference = new ServerLlmInferenceApi(caller); diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSandboxApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSandboxApi.java index 1484140f6a..2db9adfedd 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSandboxApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSandboxApi.java @@ -23,11 +23,138 @@ public final class ServerSandboxApi { /** API methods for the {@code sandbox.proxyCa} sub-namespace. */ public final ServerSandboxProxyCaApi proxyCa; + /** API methods for the {@code sandbox.proxySecret} sub-namespace. */ + final ServerSandboxProxySecretApi proxySecret; + /** API methods for the {@code sandbox.settings} sub-namespace. */ + final ServerSandboxSettingsApi settings; /** @param caller the RPC transport function */ ServerSandboxApi(RpcCaller caller) { this.caller = caller; this.proxyCa = new ServerSandboxProxyCaApi(caller); + this.proxySecret = new ServerSandboxProxySecretApi(caller); + this.settings = new ServerSandboxSettingsApi(caller); + } + + /** + * Raises a user's sandbox settings to an administrator's managed sandbox floor and returns the result a session should actually run under. The floor can only tighten: an enterprise force-on is reflected even when the user's own `enabled` is false, and a user cannot lower a setting the floor raised. The result is normalized through the declared sandbox shape, so a key reorder or an undeclared passthrough key is not mistaken for a policy change by callers that diff two results. A host must call this rather than merge the two layers itself, because reimplementing the direction of the fold is what turns an administrator's floor into a suggestion. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture resolveFlooredConfig(SandboxResolveFlooredConfigParams params) { + return caller.invoke("sandbox.resolveFlooredConfig", params, SandboxResolveFlooredConfigResult.class); + } + + /** + * Builds the effective filesystem policy one `@`-file picker checks its suggestions against, so Git discovery and grant canonicalization run once for the picker's lifetime instead of on every debounced keystroke. Returns no policy when the sandbox is absent or disabled, which the paired filter reads as "keep every candidate". A host must pair this with `sandbox.filterPickerCandidates` rather than resolve a grant list itself, because the picker's grants are narrower than a file tool's — the same path a built-in tool may read can be one the picker must not offer. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture preparePickerPolicy(SandboxPreparePickerPolicyParams params) { + return caller.invoke("sandbox.preparePickerPolicy", params, SandboxPreparePickerPolicyResult.class); + } + + /** + * Checks a batch of `@`-file picker suggestions against a policy prepared by `sandbox.preparePickerPolicy` and returns one allow decision per candidate, positionally aligned with the request. Batched deliberately: each check canonicalizes a path component-by-component through the real filesystem, so one call amortizes that over a single hop rather than dozens while the user types. A directory the policy does not itself allow can still be kept as a waypoint to an allowed subtree. An absent policy keeps every candidate. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture filterPickerCandidates(SandboxFilterPickerCandidatesParams params) { + return caller.invoke("sandbox.filterPickerCandidates", params, SandboxFilterPickerCandidatesResult.class); + } + + /** + * The sandbox a single named feature actually launches under, which is not the same as the session's sandbox. A feature is routed out of the sandbox by its own flag — `sandboxMcpServers`, `sandboxLspServers` — so an enabled session can still start one server confined and another free. Returns a disabled sandbox rather than an error when the host has no sandbox slice at all, so an absent config and an opted-out feature read alike at the launch site. A host must ask per feature rather than reuse the session config, because reading only `enabled` confines a server the user opted out of, or reports as sandboxed one that is not. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture resolveForOptional(SandboxResolveForOptionalParams params) { + return caller.invoke("sandbox.resolveForOptional", params, SandboxEffectiveForOptionalResult.class); + } + + /** + * Reports whether this machine can run an OS-level command sandbox at all. A host must ask before it offers sandboxing, because the answer is a property of the machine rather than of any setting: on Linux it depends on bubblewrap being installed, and on Windows on the OS build supporting BaseContainer. Probing it requires spawning a process the first time, so the answer is cached for the life of the runtime. A host that assumes support and enables the sandbox anyway produces a session in which every shell command fails. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture checkPlatformSupport() { + return caller.invoke("sandbox.checkPlatformSupport", java.util.Map.of(), Boolean.class); + } + + /** + * Reports whether this Windows host's BaseContainer implementation can reach the host's loopback interface directly, which is what a local-network policy needs to avoid routing through the egress proxy. Like `sandbox.checkPlatformSupport`, the answer is a property of the machine's OS build rather than of any setting, and resolving it the first time spawns a platform probe. A host that offers the direct local-network option on a machine that lacks the capability produces a session whose sandboxed commands cannot reach local services. Always false off Windows. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture checkWindowsHostLoopbackSupport() { + return caller.invoke("sandbox.checkWindowsHostLoopbackSupport", java.util.Map.of(), Boolean.class); + } + + /** + * The explanation a host shows when it refuses an explicit request to turn the sandbox on for a machine that cannot run one. Carries the platform-specific remedy — install bubblewrap, update Windows — and the documentation link, so a host that got `false` from `sandbox.checkPlatformSupport` does not have to compose its own reason and cannot describe the same machine differently from the runtime's own spawn path. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture getUnsupportedMessage() { + return caller.invoke("sandbox.getUnsupportedMessage", java.util.Map.of(), String.class); + } + + /** + * The notice a host shows when a stored `sandbox` setting is being ignored for this session because the machine cannot enforce it. Distinct from `sandbox.getUnsupportedMessage`, which refuses an explicit request: this one reports that a setting the user already saved is inert, states that commands will run unsandboxed, and confirms the stored setting itself is left alone. A host that stays silent here leaves the user believing a sandbox is active. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture getUnsupportedSettingDisabledNotice() { + return caller.invoke("sandbox.getUnsupportedSettingDisabledNotice", java.util.Map.of(), String.class); + } + + /** + * The warning a host prints at startup when a session asked for a sandbox on a machine that cannot run one. The third of the unsupported notices, and the only one that depends on the policy in force: it names the remedy the reader can actually act on, which is the stored setting for an unmanaged host, an administrator for a managed one, and waiting for the policy to resolve when the sandbox stayed on only because the policy could not be determined. Returns no notice when the machine does support a sandbox, so a host can call it unconditionally on the startup path. A host that composes its own warning tells a managed user to change a setting their policy will overwrite. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture getUnsupportedStartupNotice(SandboxGetUnsupportedStartupNoticeParams params) { + return caller.invoke("sandbox.getUnsupportedStartupNotice", params, SandboxUnsupportedStartupNoticeResult.class); + } + + /** + * Whether one slash command stays reachable while sandbox enforcement is unavailable and the session is otherwise blocked. The permitted set is deliberately narrow — commands that only report state, and the few that let the user repair or leave the situation — and it is keyed on the arguments as well as the name, so `/sandbox status` is admitted where `/sandbox enable` is not. A host must ask rather than keep its own list: a command this returns false for is one the runtime would refuse anyway, and a divergent list either strands the user with no way out or admits a command that acts on an unenforced session. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture checkEnforcementCommand(SandboxCheckEnforcementCommandParams params) { + return caller.invoke("sandbox.checkEnforcementCommand", params, Boolean.class); + } + + /** + * Returns the documentation URL a host should point a user at when it declines or downgrades a sandbox setting. The runtime owns the address so every surface cites the same page and a host does not hard-code a link that later moves. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture getDocsUrl() { + return caller.invoke("sandbox.getDocsUrl", java.util.Map.of(), SandboxDocsUrlResult.class); } /** diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSandboxProxyCaApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSandboxProxyCaApi.java index 0cc59738e9..5559a0dbcb 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSandboxProxyCaApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSandboxProxyCaApi.java @@ -26,6 +26,28 @@ public final class ServerSandboxProxyCaApi { this.caller = caller; } + /** + * Reports whether this runtime process can offer installing the sandbox credential-proxy certificate authority into OS trust. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture checkInstallationSupport() { + return caller.invoke("sandbox.proxyCa.checkInstallationSupport", java.util.Map.of(), Boolean.class); + } + + /** + * Reports whether this sandbox configuration uses the credential proxy and can need certificate-authority setup. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture checkRelevance(SandboxProxyCaCheckRelevanceParams params) { + return caller.invoke("sandbox.proxyCa.checkRelevance", params, Boolean.class); + } + /** * Reports whether the persistent certificate authority of the sandbox credential proxy exists, whether OS trust includes it, and whether it must be rotated. Changes nothing. * diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSandboxProxySecretApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSandboxProxySecretApi.java new file mode 100644 index 0000000000..1432755fac --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSandboxProxySecretApi.java @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import java.util.concurrent.CompletableFuture; +import javax.annotation.processing.Generated; + +/** + * API methods for the {@code sandbox.proxySecret} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +final class ServerSandboxProxySecretApi { + + private final RpcCaller caller; + + /** @param caller the RPC transport function */ + ServerSandboxProxySecretApi(RpcCaller caller) { + this.caller = caller; + } + + /** + * Reports where this runtime keeps the sandbox proxy password: the credential store directory and the entry identifier within it. Both are derived from the configuration directory this runtime was started with, so a host does not resolve them itself. Read this to recognize a `${secret:…}` value in stored sandbox settings as this runtime's own proxy password rather than a reference the user wrote by hand. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture getLocation() { + return caller.invoke("sandbox.proxySecret.getLocation", java.util.Map.of(), SandboxProxySecretLocation.class); + } + + /** + * Reads the stored sandbox proxy password from the credential store `sandbox.proxySecret.getLocation` names, preferring the OS keychain and falling back to the on-disk store. Reports an absent value rather than failing when no entry exists. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture get() { + return caller.invoke("sandbox.proxySecret.get", java.util.Map.of(), SandboxProxySecretGetResult.class); + } + + /** + * Stores the sandbox proxy password in the credential store `sandbox.proxySecret.getLocation` names, replacing any existing entry. Reports whether the OS keychain accepted it or it fell back to the on-disk store. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture save(SandboxProxySecretSaveParams params) { + return caller.invoke("sandbox.proxySecret.save", params, SandboxProxySecretSaveResult.class); + } + + /** + * Removes the stored sandbox proxy password from the credential store `sandbox.proxySecret.getLocation` names. Succeeds when no entry exists, so it is safe to call while clearing a proxy password whose origin is unknown. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture delete() { + return caller.invoke("sandbox.proxySecret.delete", java.util.Map.of(), Void.class); + } + +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSandboxSettingsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSandboxSettingsApi.java new file mode 100644 index 0000000000..4d323a03ac --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSandboxSettingsApi.java @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.databind.JsonNode; +import com.github.copilot.CopilotExperimental; +import java.util.concurrent.CompletableFuture; +import javax.annotation.processing.Generated; + +/** + * API methods for the {@code sandbox.settings} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +final class ServerSandboxSettingsApi { + + private final RpcCaller caller; + + /** @param caller the RPC transport function */ + ServerSandboxSettingsApi(RpcCaller caller) { + this.caller = caller; + } + + /** + * Resolves a stored sandbox settings object into the effective one, filling every value the user left unset with its default. Read this to render or reason about the sandbox configuration a session would actually run under, rather than the sparse document the user wrote. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture applyDefaults(SandboxSettingsApplyDefaultsParams params) { + return caller.invoke("sandbox.settings.applyDefaults", params, JsonNode.class); + } + +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsApi.java index 30b35346bb..a3e7dc7c58 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsApi.java @@ -8,6 +8,7 @@ package com.github.copilot.generated.rpc; import com.github.copilot.CopilotExperimental; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import javax.annotation.processing.Generated; @@ -99,4 +100,17 @@ public CompletableFuture getDiscoveryPaths(Skills return caller.invoke("skills.getDiscoveryPaths", params, SkillsGetDiscoveryPathsResult.class); } + /** + * 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. + *

+ * Accepts the extensible request, including inputs added after the params record. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture getDiscoveryPaths(SkillsGetDiscoveryPathsRequest request) { + return caller.invoke("skills.getDiscoveryPaths", Objects.requireNonNull(request, "request"), SkillsGetDiscoveryPathsResult.class); + } + } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerUserSettingsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerUserSettingsApi.java index 9c5656ff14..75c8e38934 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerUserSettingsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerUserSettingsApi.java @@ -7,6 +7,7 @@ package com.github.copilot.generated.rpc; +import com.fasterxml.jackson.databind.JsonNode; import com.github.copilot.CopilotExperimental; import java.util.concurrent.CompletableFuture; import javax.annotation.processing.Generated; @@ -26,6 +27,160 @@ public final class ServerUserSettingsApi { this.caller = caller; } + /** + * Adds or removes one hook key in the user `disabledHooks` setting, rewriting settings.json in place and leaving every other setting untouched. Reports a typed outcome rather than failing the call, so a host can tell an unparseable settings file apart from a write that could not complete. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture setHookDisabled(UserSettingsSetHookDisabledParams params) { + return caller.invoke("user.settings.setHookDisabled", params, UserSettingsSetHookDisabledResult.class); + } + + /** + * Reads the user's settings document exactly as `user.settings.load` does, but from a caller-supplied configuration directory instead of the one the server resolved for itself. Use this when a consumer scopes a session to its own Copilot home — the SDK's per-session `configDir` override — so the settings read match the directory that session actually uses. An absent or empty `configDir` resolves the server's own home, making this identical to `user.settings.load`. Hook-validation relaxations are deliberately not caller-supplied: they continue to come from the server's environment, so a consumer cannot widen the host's hook policy through this method. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture loadForConfigDir(UserSettingsLoadForConfigDirParams params) { + return caller.invoke("user.settings.loadForConfigDir", params, JsonNode.class); + } + + /** + * Reads a repository's checked-in settings document as stored, normalized, together with any diagnostic raised while reading it. This is the repository half of the layering `user.settings.get` reports: `user.settings.load` reads the user's own document, and this reads the one a repository checks in to govern the keys `user.settings.governanceKeys` lists as repo-governable. A document that fails to parse, or that sets a key a repository may not govern, is reported through `warning` while the settings that did load are still returned, so a consumer surfaces the diagnostic and continues rather than failing the session. When `repoRoot` is the user's home directory the Claude-compatible configuration is skipped, so a home-directory session does not inherit a personal config as repository policy. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture loadRepo(UserSettingsLoadRepoParams params) { + return caller.invoke("user.settings.loadRepo", params, UserSettingsLoadRepoResult.class); + } + + /** + * Reads a repository's settings and merges them over a supplied user layer in one step, returning the settings a consumer should act on inside that repository. Use this rather than reading the two layers separately: the merge is per setting and collection-aware, so a repository adds to the collections it is allowed to influence without replacing the user's own entries, and a whole-document overlay would get that wrong. Any diagnostic raised while reading the repository layer is returned rather than printed, so the consumer decides how to surface it; the settings that did load are merged regardless. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture loadAndMergeRepo(UserSettingsLoadAndMergeRepoParams params) { + return caller.invoke("user.settings.loadAndMergeRepo", params, UserSettingsLoadAndMergeRepoResult.class); + } + + /** + * Reports the on-disk path of the settings document `user.settings.load` and `user.settings.set` read and write. Resolved the same way those methods resolve it, so a host that shows or opens the settings file names the file the runtime actually uses, including the extensionless legacy `settings` file when that is what is present. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture getPath() { + return caller.invoke("user.settings.getPath", java.util.Map.of(), UserSettingsPathResult.class); + } + + /** + * Reads the user's stored settings together with the diagnostic raised while reading them. This is `user.settings.load` plus the reason part of the document was ignored: that one answers the document alone, so a caller cannot tell a clean read from a salvaged one. Read this when the caller is about to write the file back, where accepting a partial parse would discard every value the loader could not read. The settings that did load are still reported alongside the warning, so a consumer may surface it and continue instead of failing. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture loadWithWarning() { + return caller.invoke("user.settings.loadWithWarning", java.util.Map.of(), UserSettingsLoadWithWarningResult.class); + } + + /** + * Folds a user settings layer over a managed (enterprise policy) layer and returns the merged settings. The managed layer's governance keys win, `autoTier` resolves through its overridable form, and the two per-entry control maps (`enabledPlugins`, `extraKnownMarketplaces`) union across the layers and are then stamped back from the managed layer, so each entry an administrator declares wins while the user keeps control of the rest. The parameters are named for their layers rather than being a generic base/override pair: nothing about a settings object identifies which layer produced it, so the runtime cannot detect a transposed call, and transposing one resolves the governance keys in the user's favour instead of the administrator's. Supplying only one layer returns it unchanged, and supplying neither returns an empty object. Rejects a managed `autoTier` that is neither a user-configurable tier nor a single-key overridable form, rather than silently dropping it. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture mergeManagedAndUser(UserSettingsMergeManagedAndUserParams params) { + return caller.invoke("user.settings.mergeManagedAndUser", params, UserSettingsMergeManagedAndUserResult.class); + } + + /** + * Folds a repository's settings layer into the user layer. Each repo-overridable key carries a directional policy chosen so a repository can only add restrictions and never relax the user's own. The two per-entry managed control maps are the exception those directional rules cannot express — a repository may legitimately enable plugins and declare marketplaces — so they are re-stamped afterwards from the runtime's ambient managed policy. That makes the result depend on the runtime process's managed state and not on the arguments alone: a host must call this rather than reimplement the fold, or a repository could relax a restriction the administrator imposed. Rejects when either layer is not a JSON object. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture mergeRepoValue(UserSettingsMergeRepoValueParams params) { + return caller.invoke("user.settings.mergeRepoValue", params, UserSettingsMergeRepoValueResult.class); + } + + /** + * Runs the one-time migration that moves a staff account's update channel to stable, and records that it ran. Idempotent and self-gating: it takes a lock over the global-state file, re-reads the completion stamp inside it, and returns `current` rather than migrating twice, so two hosts racing at startup cannot both migrate. Only a `migrated` status means the persisted settings changed and the caller should reload them. An account on the experimental channel is deliberately left alone and reported as `excluded-experimental`. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture migrateStaffUpdateChannelOnce(UserSettingsMigrateStaffUpdateChannelOnceParams params) { + return caller.invoke("user.settings.migrateStaffUpdateChannelOnce", params, UserSettingsMigrateStaffUpdateChannelOnceResult.class); + } + + /** + * Runs the one-time reset that clears a staff account's pinned model selection, and reports the timestamp the host should record. Idempotent and self-gating: the reset is gated on a fixed cutoff, so a caller that passes back the timestamp it recorded gets `didReset: false` rather than a second reset. It applies to staff accounts only; any other account is reported as not reset without the settings document being opened. Only `didReset: true` means the persisted settings changed, and only then are the updated settings returned for the host to adopt. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture resetStaffModelSelectionOnce(UserSettingsResetStaffModelSelectionOnceParams params) { + return caller.invoke("user.settings.resetStaffModelSelectionOnce", params, UserSettingsResetStaffModelSelectionOnceResult.class); + } + + /** + * Reports the top-level keys in the user settings document that the runtime does not recognize, phrased for display to the user. This is a separate question from the warning `user.settings.loadWithWarning` returns: that one describes parts of the document the loader had to ignore to load the rest, whereas an unknown key parses cleanly and is simply not a setting, so a host that surfaces only the load warning silently accepts a misspelled key that will never take effect. Nothing is reported when the document is absent, unreadable, or failed to load, because an unknown-key list derived from a document the runtime could not read would be misleading rather than helpful. Inspection never modifies the document. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture getUnknownKeysWarning(UserSettingsGetUnknownKeysWarningParams params) { + return caller.invoke("user.settings.getUnknownKeysWarning", params, UserSettingsUnknownKeysWarningResult.class); + } + + /** + * Resolves the effective runtime settings for a host that is about to start a session, layering the given overrides over the settings documents, the environment and the platform defaults. A host cannot assemble this itself: the precedence between a user document, a repository document, the managed layers and the environment is the runtime's own rule, and a host that re-implements it drifts from the server it is configuring. The working directory, environment and platform are taken as arguments rather than read from the server process, so a host resolving settings for a directory other than its own gets that directory's answer. Nothing is written; this only computes. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture buildRuntime(UserSettingsBuildRuntimeParams params) { + return caller.invoke("user.settings.buildRuntime", params, JsonNode.class); + } + + /** + * Runs the one-time migration that moves a staff account's log level to the current default, and records that it ran. Idempotent and self-gating: it takes a lock over the global-state file and re-reads the completion stamp inside it, so two hosts racing at startup cannot both migrate. Only `refreshed` can have changed the persisted settings, and the settings it returns are the document re-read after the write, so a host should adopt them rather than reloading separately and racing the next writer. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture migrateStaffLogLevelOnce(UserSettingsMigrateStaffLogLevelOnceParams params) { + return caller.invoke("user.settings.migrateStaffLogLevelOnce", params, UserSettingsMigrateStaffLogLevelOnceResult.class); + } + + /** + * Runs the one-time cleanup that removes obsolete CLI-owned settings and legacy rubber-duck keys from the user's settings document, and reports what it did. Idempotent and safe to call on every startup: with nothing obsolete left it reports `unchanged` and writes nothing. The cleanup preserves every key it does not recognize, so a setting written by a newer host is never dropped by an older one, and a document that fails to parse is reported `invalid` and left untouched rather than rewritten from a partial read. The cleaned document is not returned: it is written before this replies, so `user.settings.load` reads it authoritatively. A host that surfaces unrecognized keys through `user.settings.unknownKeys` wants this first, because the keys this retires would otherwise be shown to the user as if they were misspelled settings. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture migrateObsoleteOnDisk(UserSettingsMigrateObsoleteOnDiskParams params) { + return caller.invoke("user.settings.migrateObsoleteOnDisk", params, UserSettingsMigrateObsoleteOnDiskResult.class); + } + /** * Lists every known user setting from settings.json, each with its effective value, its default, and whether it is at the default — so settings the user has never set still appear with their default value. Does not include repository- or enterprise-managed overrides that the runtime layers on top at session time. * @@ -37,6 +192,17 @@ public CompletableFuture get() { return caller.invoke("user.settings.get", java.util.Map.of(), UserSettingsGetResult.class); } + /** + * Resolves the on-disk locations of the settings files this runtime reads, so a consumer can show or open them. The user path is the same file `user.settings.get`, `user.settings.load`, and `user.settings.set` operate on. Supply `repoRoot` to also resolve that repository's checked-in and local settings paths. A path is reported whether or not the file exists. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture getPaths(UserSettingsGetPathsParams params) { + return caller.invoke("user.settings.getPaths", params, UserSettingsGetPathsResult.class); + } + /** * Writes one or more user settings to settings.json, replacing each provided top-level key. A key whose value is null is removed. * @@ -48,4 +214,103 @@ public CompletableFuture set(UserSettingsSetParams params) { return caller.invoke("user.settings.set", params, Void.class); } + /** + * Lists the top-level user-setting keys by who is allowed to govern them: `repo` keys a repository may override through `.github/copilot/settings.json`, `claudeRepo` keys a repository may override through a Claude-compatible config, and `managed` keys an enterprise policy may fix. The lists are static for a given runtime build and fail closed: a key absent from a list cannot be governed that way. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture listGovernanceKeys() { + return caller.invoke("user.settings.listGovernanceKeys", java.util.Map.of(), UserSettingsGovernanceKeysResult.class); + } + + /** + * Atomically edits one canonical user setting inside its settings-file lock. Reads, canonicalizes touched aliases, applies the edit, validates the resulting top-level setting and writes before releasing the lock. An explicit autoUpdatesChannel set completes the staff migration in the same transaction. Only a malformed existing file returns `invalid`; refused keys, invalid values and I/O errors reject the call. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture edit(UserSettingsEditParams params) { + return caller.invoke("user.settings.edit", params, SettingsEditResult.class); + } + + /** + * Rewrites legacy and snake_case setting names in a settings object to their canonical spelling. A caller editing settings.json must normalize before writing: a legacy key left in place survives the write, is re-normalized on the next load, and silently shadows the canonical value the write just stored. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture normalize(UserSettingsNormalizeParams params) { + return caller.invoke("user.settings.normalize", params, UserSettingsNormalizeResult.class); + } + + /** + * Diagnoses a set of already-loaded settings layers, reporting each key that will not take effect and why. Settings load tolerantly so that one bad key does not stop a session from starting; this method surfaces what was dropped so a caller can report it instead of silently ignoring the user's intent. Takes the layers as given rather than reading them, so a caller can diagnose exactly the content it is showing, including unsaved edits. Never echoes a setting's value, because a value may be a token or another secret. Marked internal because it exists to carry a CLI call site off the native addon, not to serve a consumer-facing need; it becomes public once an SDK consumer needs to inspect settings health itself. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture diagnoseProblems(UserSettingsDiagnoseProblemsParams params) { + return caller.invoke("user.settings.diagnoseProblems", params, UserSettingsProblemsResult.class); + } + + /** + * Validates one top-level setting subtree and writes just that key to settings.json as a single step. Doing both here closes the race a caller cannot close itself: between a separate preflight read and the write, the file may change. Rewriting only the touched key keeps legacy siblings out of settings.json. Answers a status rather than failing, so a validation problem and a write problem stay distinguishable. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture applyTopKey(UserSettingsApplyTopKeyParams params) { + return caller.invoke("user.settings.applyTopKey", params, UserSettingsApplyTopKeyResult.class); + } + + /** + * Replaces the user settings file's entire content in one write. A key absent from `settings` is removed, which is what distinguishes this from `user.settings.set`: that one merges, so it can never clear a key the caller has dropped. For a caller saving the result of a raw edit, a merge would silently resurrect every key the user deleted. Records the same one-time mutation marks a per-key write records, so a setting the user has now chosen explicitly stops being migrated for them. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture writeAll(UserSettingsWriteAllParams params) { + return caller.invoke("user.settings.writeAll", params, Void.class); + } + + /** + * Merges a partial object into one top-level user setting, re-reading the stored value inside this runtime process's settings.json write lock so that concurrent writers in this process to different sub-fields of the same key do not clobber each other. Unlike `user.settings.set`, which replaces a key's whole value with what the caller last read, this keeps sub-fields the caller did not name when both the stored value and supplied value are objects; otherwise the supplied value replaces the stored value. Use it whenever two independent surfaces own different sub-fields of one setting. Returns the key's stored value after the write, in `value`. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture update(UserSettingsUpdateParams params) { + return caller.invoke("user.settings.update", params, UserSettingsUpdateResult.class); + } + + /** + * Reads settings.json as stored for a caller that is about to edit it. Unlike `user.settings.get`, this returns file values rather than per-key metadata and defaults. Reports `missing` for an absent file and `invalid` when the existing file will not parse. Retired config.json and extensionless files are not read. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture loadForEdit() { + return caller.invoke("user.settings.loadForEdit", java.util.Map.of(), UserSettingsLoadForEditResult.class); + } + + /** + * Validates one top-level setting subtree without writing it, reporting each problem with the path it applies to. Scoped to a single top-level key on purpose: validating the whole file would reject an edit whenever an unrelated pre-existing field is malformed, locking the user out of the very tool that could fix it. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture validate(UserSettingsValidateParams params) { + return caller.invoke("user.settings.validate", params, UserSettingsValidateResult.class); + } + } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasProviderRegisterParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasProviderRegisterParams.java index a9213671aa..4843040c6e 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasProviderRegisterParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasProviderRegisterParams.java @@ -30,6 +30,7 @@ record SessionCanvasProviderRegisterParams( /** Connection identifier for callback routing */ @JsonProperty("connectionId") String connectionId, /** Provider metadata supplied by the host */ + @JsonInclude(JsonInclude.Include.ALWAYS) @JsonProperty("info") Object info, /** Canvas contributions supplied by the provider */ @JsonProperty("canvases") List canvases diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthApi.java index 388576154a..f98aa7791d 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthApi.java @@ -138,8 +138,8 @@ CompletableFuture switchToAuth(SessionGitHubAuthSwitchToAuthParams params) * @since 1.0.0 */ @CopilotExperimental - CompletableFuture logout() { - return caller.invoke("session.gitHubAuth.logout", java.util.Map.of("sessionId", this.sessionId), Void.class); + CompletableFuture logout() { + return caller.invoke("session.gitHubAuth.logout", java.util.Map.of("sessionId", this.sessionId), Boolean.class); } /** @@ -152,10 +152,10 @@ CompletableFuture logout() { * @since 1.0.0 */ @CopilotExperimental - CompletableFuture logoutUser(SessionGitHubAuthLogoutUserParams params) { + CompletableFuture logoutUser(SessionGitHubAuthLogoutUserParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); - return caller.invoke("session.gitHubAuth.logoutUser", _p, Void.class); + return caller.invoke("session.gitHubAuth.logoutUser", _p, Boolean.class); } /** diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionManagedSettingsGetResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionManagedSettingsGetResult.java index faf08c57d6..cdd674ab24 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionManagedSettingsGetResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionManagedSettingsGetResult.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.github.copilot.CopilotExperimental; +import com.github.copilot.generated.ManagedPermissionsContext; import java.util.List; import javax.annotation.processing.Generated; @@ -25,6 +26,8 @@ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public record SessionManagedSettingsGetResult( + /** Reusable retained managed permission policy for managedSettings.permissions.evaluate, including source composition and session-local injection. Provided by live session snapshots; absent in older events. Refresh the context when managed policy changes. */ + @JsonProperty("permissionsContext") ManagedPermissionsContext permissionsContext, /** Channel summary: `server`, `device`, `client`, or `policyHelper` when exactly one channel contributed; `mixed` when multiple channels contributed; otherwise `none`. Consult the per-channel booleans for exact provenance. */ @JsonProperty("source") ManagedSettingsResolvedSource source, /** Whether the server (account/org) managed-settings layer was present */ @@ -35,11 +38,11 @@ public record SessionManagedSettingsGetResult( @JsonProperty("clientManaged") Boolean clientManaged, /** Whether the policy-helper managed-settings layer was present. The policy helper is the weakest channel: it fills keys no enterprise source set and can never replace one. */ @JsonProperty("policyHelperManaged") Boolean policyHelperManaged, - /** Whether managed policy could not be determined (e.g. a failed server fetch) and the session fell back to the fail-closed restriction. When true, restrictions such as disabling bypass-permissions are enforced even though `settings` may be absent. */ + /** Whether managed policy could not be determined (e.g. a failed server fetch) and unresolved-policy safeguards remain active. This does not by itself disable bypass-permissions; `bypassPermissionsDisabled` reports only an explicit policy restriction. */ @JsonProperty("failClosed") Boolean failClosed, /** Whether the effective sandbox policy forces the sandbox on *only* because managed policy could not be determined, rather than because the policy requires it. Lets clients tell a user whose `--no-sandbox` was overridden that the sandbox stayed on as a fail-closed fallback, instead of attributing it to an administrator who set no such policy. */ @JsonProperty("sandboxEnabledByUndeterminedPolicy") Boolean sandboxEnabledByUndeterminedPolicy, - /** Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers, and forced on when `failClosed` is true. */ + /** Whether an explicit enterprise policy restriction disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers; an unresolved policy does not force this on by itself. */ @JsonProperty("bypassPermissionsDisabled") Boolean bypassPermissionsDisabled, /** Whether at least two managed sources supplied permission allowlists, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. */ @JsonProperty("permissionsAllowIntersected") Boolean permissionsAllowIntersected, diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpExecuteSamplingParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpExecuteSamplingParams.java index 54950ec4f4..7978c03553 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpExecuteSamplingParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpExecuteSamplingParams.java @@ -31,6 +31,7 @@ public record SessionMcpExecuteSamplingParams( /** Name of the MCP server that initiated the sampling request */ @JsonProperty("serverName") String serverName, /** The original MCP JSON-RPC request ID (string or number). Used by the runtime to correlate the inference with the originating MCP request for telemetry; this is distinct from `requestId` (which is the schema-level cancellation handle). */ + @JsonInclude(JsonInclude.Include.ALWAYS) @JsonProperty("mcpRequestId") Object mcpRequestId, /** Raw MCP CreateMessageRequest params, as received in the `sampling.requested` event. Treated as opaque at the schema layer; the runtime converts the embedded MCP messages into the OpenAI chat-completion shape internally. */ @JsonProperty("request") McpExecuteSamplingRequest request diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelApi.java index 1c5c1c3d39..bfd5d8a89e 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelApi.java @@ -153,6 +153,22 @@ public CompletableFuture setReasoningEffor return caller.invoke("session.model.setReasoningEffort", _p, SessionModelSetReasoningEffortResult.class); } + /** + * Computes whether the session's current model shows reasoning summaries and how a host should present them: the default Ctrl+T visibility, reasoning headers, the hidden-summaries hint, and whether a persisted `showReasoning` setting is redundant. The runtime folds in the session's model catalog, selection and reasoning-summaries experiment assignment; the host supplies only its own persisted setting and current visibility. This method reads the session's resolved model catalog rather than resolving it, so call `session.model.list` first on a session that has not listed models or run a turn; until then the result is `ready: false`. A session that resolves models without a Copilot catalog, that is a BYOK registry or an alternate provider, never caches one and stays `ready: false`. + *

+ * Note: the {@code sessionId} field in the params record is overridden + * by the session-scoped wrapper; any value provided is ignored. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture getReasoningSummaries(SessionModelGetReasoningSummariesParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.model.getReasoningSummaries", _p, SessionModelGetReasoningSummariesResult.class); + } + /** * 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. *

diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelGetReasoningSummariesParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelGetReasoningSummariesParams.java new file mode 100644 index 0000000000..488fe67616 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelGetReasoningSummariesParams.java @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Host presentation state the reasoning-summaries policy depends on. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionModelGetReasoningSummariesParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + /** The host's persisted `showReasoning` user setting, omitted when unset. */ + @JsonProperty("persistedShowReasoning") Boolean persistedShowReasoning, + /** Whether the host is currently showing reasoning summaries. Defaults to false. */ + @JsonProperty("summariesVisible") Boolean summariesVisible +) { + + /** + * Creates host presentation state; the session-scoped wrapper supplies the session ID. + * + * @param persistedShowReasoning the persisted visibility setting, or null when unset + * @param summariesVisible whether summaries are visible, or null for the default + */ + public SessionModelGetReasoningSummariesParams(Boolean persistedShowReasoning, Boolean summariesVisible) { + this(null, persistedShowReasoning, summariesVisible); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelGetReasoningSummariesResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelGetReasoningSummariesResult.java new file mode 100644 index 0000000000..c23a1af9ea --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelGetReasoningSummariesResult.java @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Reasoning-summaries presentation policy for the session's current model. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionModelGetReasoningSummariesResult( + /** False until the session has a model catalog and a selected model. This read never resolves the catalog itself, and the runtime emits no readiness notification, so repeating this call alone leaves `ready` false indefinitely on a session that has neither listed models nor run a turn. `session.model.list` is the prerequisite and the signal: it resolves and caches the catalog for the session, so a successful return means the next read is ready. The exception is a session that resolves models without a Copilot catalog, that is a BYOK registry or an alternate provider: `session.model.list` returns an empty list without caching a catalog, so `ready` stays false and this policy is unavailable there. While false, every model-policy field is false and `visibility` is omitted; `experimentSettled` is independent of readiness and still reports the assignment state. */ + @JsonProperty("ready") Boolean ready, + /** Whether the session's reasoning-summaries experiment assignment has settled. False means this read does not wait for a pending assignment and reports the experiment-derived fields conservatively: `showHiddenHint` is false and `visibility` falls back to `persistedShowReasoning`, or is omitted when none was supplied. A host that wants the assigned answer re-reads until this is true. */ + @JsonProperty("experimentSettled") Boolean experimentSettled, + /** Whether turns on this session produce reasoning summaries, honouring an explicit session reasoning-summary override. */ + @JsonProperty("sessionShowsSummaries") Boolean sessionShowsSummaries, + /** Whether the selected model shows reasoning summaries by default. */ + @JsonProperty("modelDefaultShowsSummaries") Boolean modelDefaultShowsSummaries, + /** Whether the selected model emits reasoning headers. */ + @JsonProperty("showReasoningHeaders") Boolean showReasoningHeaders, + /** Default reasoning visibility, and only meaningful once `ready` is true: false when the session produces no summaries, else the persisted setting, else the experiment's default. While `ready` is true it is omitted only when all three are unavailable, that is when the session produces summaries, no `persistedShowReasoning` was supplied, and `experimentSettled` is false. While `ready` is false there is no model policy to report and this field is always omitted, so an absent value alone does not imply a pending experiment. */ + @JsonProperty("visibility") Boolean visibility, + /** Whether to show the hint that reasoning summaries are hidden. Always false while `experimentSettled` is false. */ + @JsonProperty("showHiddenHint") Boolean showHiddenHint, + /** Whether the supplied `persistedShowReasoning` setting only restates the model default. This reports a redundancy, not a standing instruction to delete the setting: it stays true for as long as the setting and the model default agree, including for a user who later chose that value deliberately. It exists for a one-time migration of a legacy setting written before the model default made it redundant, so a host that acts on it owns the cleanup and must gate it behind its own persisted `done` marker, delete only a setting whose value still matches the one it read, and never repeat the deletion on a later launch. Under an off-by-default reasoning-summaries experiment an explicit `showReasoning: true` opt-in also reads as redundant on a summary-producing model, so an ungated host would keep deleting that opt-in and hide summaries again on every launch. */ + @JsonProperty("clearRedundantSetting") Boolean clearRedundantSetting +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptions.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptions.java index 5d14a1458a..2ac3d57c97 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptions.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptions.java @@ -117,11 +117,17 @@ public record SessionOpenOptions( @JsonProperty("managedMcpServers") Map managedMcpServers, /** Whether to include instructions from every MCP server in the system prompt instead of only allowlisted servers. */ @JsonProperty("allowAllMcpServerInstructions") Boolean allowAllMcpServerInstructions, - /** Additional directories to search for skills. */ + /** Explicit discovery-only skill roots, not filesystem grants. Available without enableConfigDiscovery, subject to skill/managed disables. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. */ @JsonProperty("skillDirectories") List skillDirectories, + /** Explicit discovery-only agent roots, separate from customAgents and filesystem grants. Available without enableConfigDiscovery, subject to managed/source disables. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. */ + @JsonProperty("agentDirectories") List agentDirectories, + /** Explicit discovery-only instruction roots using existing SDK instruction formats, not filesystem grants. Available without enableConfigDiscovery, subject to instruction/managed disables. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. */ + @JsonProperty("instructionDirectories") List instructionDirectories, + /** Explicit discovery-only file-hook roots, separate from callback hooks and filesystem grants. Available without enableConfigDiscovery or enableFileHooks, which controls repository-origin hooks only; hook/managed disables still apply. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. */ + @JsonProperty("hookDirectories") List hookDirectories, /** Skill scan directories and descendants excluded from discovery. Supports `~`-relative paths. */ @JsonProperty("ignoredSkillsLocations") List ignoredSkillsLocations, - /** Whether skill loading is enabled. When omitted, an SDK skill provider enables skills by default. */ + /** Whether skill loading is enabled. When omitted, explicit SDK skill roots or an SDK skill provider enable skills independently of enableConfigDiscovery. */ @JsonProperty("enableSkills") Boolean enableSkills, /** Whether the requesting SDK session has a skill provider. The provider remains ephemeral and is never persisted in session options or history. When enableSkills is false, it remains bound but dormant and receives no callbacks. Cloud, relay, handoff, and raw sessions.open flows reject it because they cannot safely pre-register the callback handler. */ @JsonProperty("hasSkillProvider") Boolean hasSkillProvider, diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateParams.java index 3a30a13527..a5862263ae 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateParams.java @@ -82,8 +82,14 @@ public record SessionOptionsUpdateParams( @JsonProperty("envValueMode") OptionsUpdateEnvValueMode envValueMode, /** Whether to include instructions from every MCP server in the system prompt instead of only allowlisted servers. */ @JsonProperty("allowAllMcpServerInstructions") Boolean allowAllMcpServerInstructions, - /** Additional directories to search for skills. */ + /** Explicit discovery-only skill roots, not filesystem grants. Available without enableConfigDiscovery, subject to skill/managed disables. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. */ @JsonProperty("skillDirectories") List skillDirectories, + /** Explicit discovery-only agent roots, separate from customAgents and filesystem grants. Available without enableConfigDiscovery, subject to managed/source disables. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. */ + @JsonProperty("agentDirectories") List agentDirectories, + /** Explicit discovery-only instruction roots using existing SDK instruction formats, not filesystem grants. Available without enableConfigDiscovery, subject to instruction/managed disables. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. */ + @JsonProperty("instructionDirectories") List instructionDirectories, + /** Explicit discovery-only file-hook roots, separate from callback hooks and filesystem grants. Available without enableConfigDiscovery or enableFileHooks, which controls repository-origin hooks only; hook/managed disables still apply. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. */ + @JsonProperty("hookDirectories") List hookDirectories, /** Skill scan directories and descendants excluded from discovery. Supports `~`-relative paths. */ @JsonProperty("ignoredSkillsLocations") List ignoredSkillsLocations, /** Built-in skill names to include in this session. When specified, only these runtime-bundled skills are available. Skills from other sources with the same name remain available. Set to null to remove the allowlist restriction. */ @@ -136,13 +142,13 @@ public record SessionOptionsUpdateParams( @JsonProperty("skipEmbeddingRetrieval") Boolean skipEmbeddingRetrieval, /** Organization-level custom instructions to inject into the system prompt. */ @JsonProperty("organizationCustomInstructions") String organizationCustomInstructions, - /** Whether to enable loading of `.github/hooks/` filesystem hooks. Separate from the SDK callback hook mechanism. */ + /** Whether to enable repository-origin file hooks from `.github/hooks/` and trusted repository-configured roots. Does not control explicit hookDirectories or SDK callback hooks. */ @JsonProperty("enableFileHooks") Boolean enableFileHooks, /** Whether to enable host git operations (context resolution, child repo scanning, git info in system prompt). */ @JsonProperty("enableHostGitOperations") Boolean enableHostGitOperations, /** Whether to enable cross-session store writes and reads. */ @JsonProperty("enableSessionStore") Boolean enableSessionStore, - /** Whether skill loading is enabled. Explicit false disables every source, including a bound SDK provider; changing the value invalidates the loaded skill snapshot. When omitted, creation falls back to enableConfigDiscovery unless an SDK skill provider is registered. */ + /** Whether skill loading is enabled. Explicit false disables every source, including explicit SDK roots and a bound SDK provider; changing the value invalidates the loaded skill snapshot. When omitted, creation falls back to enableConfigDiscovery unless explicit SDK skill roots or an SDK skill provider are supplied. */ @JsonProperty("enableSkills") Boolean enableSkills, /** Context tier for models with tiered pricing. The session uses this to derive effective `modelCapabilitiesOverrides` so compaction, truncation, token display, and request limits honor the selected tier. */ @JsonProperty("contextTier") OptionsUpdateContextTier contextTier, diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsApi.java index e041a149c1..3235108161 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsApi.java @@ -154,4 +154,33 @@ public CompletableFuture reload(SessionPluginsReloadParams params) { return caller.invoke("session.plugins.reload", _p, Void.class); } + /** + * Installs or updates the plugins the session's managed settings require now, without waiting for the next automatic attempt after a failure, then reloads plugins. Reports progress and failures with the same `session.info` and `session.warning` events as automatic preparation. Fails when the runtime does not prepare managed plugins for the session. + *

+ * Invokes the method with no params, applying the runtime defaults. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture retryManaged() { + return retryManaged(null); + } + + /** + * Installs or updates the plugins the session's managed settings require now, without waiting for the next automatic attempt after a failure, then reloads plugins. Reports progress and failures with the same `session.info` and `session.warning` events as automatic preparation. Fails when the runtime does not prepare managed plugins for the session. + *

+ * Note: the {@code sessionId} field in the params record is overridden + * by the session-scoped wrapper; any value provided is ignored. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture retryManaged(SessionPluginsRetryManagedParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = params == null ? MAPPER.createObjectNode() : MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.plugins.retryManaged", _p, SessionPluginsRetryManagedResult.class); + } + } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsRetryManagedParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsRetryManagedParams.java new file mode 100644 index 0000000000..24b687ca75 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsRetryManagedParams.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Request parameters for the {@code session.plugins.retryManaged} RPC method. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionPluginsRetryManagedParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + /** "plugin@marketplace" specs to retry. Omit to retry every plugin the session's managed settings require. */ + @JsonProperty("plugins") List plugins +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsRetryManagedResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsRetryManagedResult.java new file mode 100644 index 0000000000..f2fc9b73cb --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsRetryManagedResult.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Per-plugin outcomes of retrying the plugins required by the session's managed settings. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionPluginsRetryManagedResult( + /** One entry per requested plugin, in request order. When no plugins were requested, one entry per plugin the managed settings require. */ + @JsonProperty("plugins") List plugins +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueConsumeSystemNotificationsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueConsumeSystemNotificationsParams.java index 3b1d844881..850afce76e 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueConsumeSystemNotificationsParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueConsumeSystemNotificationsParams.java @@ -27,6 +27,7 @@ record SessionQueueConsumeSystemNotificationsParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Opaque runtime-owned filter object. */ + @JsonInclude(JsonInclude.Include.ALWAYS) @JsonProperty("filter") Object filter ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionRpc.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionRpc.java index 21050a5317..9a56863901 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionRpc.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionRpc.java @@ -280,8 +280,8 @@ public CompletableFuture interruptMainTurn(Sessi * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture cancelAllBackgroundAgents() { - return caller.invoke("session.cancelAllBackgroundAgents", java.util.Map.of("sessionId", this.sessionId), Void.class); + public CompletableFuture cancelAllBackgroundAgents() { + return caller.invoke("session.cancelAllBackgroundAgents", java.util.Map.of("sessionId", this.sessionId), Long.class); } /** diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSandboxApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSandboxApi.java index 2ace81bb8e..b780e59b38 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSandboxApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSandboxApi.java @@ -41,6 +41,17 @@ public CompletableFuture getEnforcemen return caller.invoke("session.sandbox.getEnforcementStatus", java.util.Map.of("sessionId", this.sessionId), SessionSandboxGetEnforcementStatusResult.class); } + /** + * Lists possible secret-bearing environment variables available to new sandboxed shells that are not already covered by credential masking. Uses local name rules and known-provider host suggestions, never returns secret values, and changes no settings or network permissions. Returns no suggestions while the session is not sandboxed. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture getCredentialSuggestions() { + return caller.invoke("session.sandbox.getCredentialSuggestions", java.util.Map.of("sessionId", this.sessionId), SessionSandboxGetCredentialSuggestionsResult.class); + } + /** * Disables sandboxing for the remainder of the current session and approves the referenced pending sandbox-bypass permission request. The request is rejected unless the exact request is still pending and the effective sandbox policy permits bypass. *

diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSandboxGetCredentialSuggestionsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSandboxGetCredentialSuggestionsParams.java new file mode 100644 index 0000000000..c587908619 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSandboxGetCredentialSuggestionsParams.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Identifies the target session. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionSandboxGetCredentialSuggestionsParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSandboxGetCredentialSuggestionsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSandboxGetCredentialSuggestionsResult.java new file mode 100644 index 0000000000..2f883728f1 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSandboxGetCredentialSuggestionsResult.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Possible unconfigured secrets in the sandbox shell environment, sorted by variable name, with no secret values. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionSandboxGetCredentialSuggestionsResult( + /** Candidates for user review. Empty when sandboxing is disabled or no unconfigured candidates are present. */ + @JsonProperty("suggestions") List suggestions +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsExecuteParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsExecuteParams.java index e60023c3aa..0e118ff815 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsExecuteParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsExecuteParams.java @@ -29,6 +29,7 @@ public record SessionToolsExecuteParams( /** Name of the currently offered tool to execute. */ @JsonProperty("name") String name, /** Arguments supplied to the tool. */ + @JsonInclude(JsonInclude.Include.ALWAYS) @JsonProperty("arguments") Object arguments, /** Optional identifier used to correlate this invocation with its tool call. */ @JsonProperty("toolCallId") String toolCallId diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsTaskCompleteEventDataParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsTaskCompleteEventDataParams.java index 9902fb8a99..56fbf48a11 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsTaskCompleteEventDataParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsTaskCompleteEventDataParams.java @@ -27,6 +27,7 @@ public record SessionToolsTaskCompleteEventDataParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Arguments supplied to the completed task_complete tool call. */ + @JsonInclude(JsonInclude.Include.ALWAYS) @JsonProperty("toolArgs") Object toolArgs, /** Final expanded result returned by the task_complete tool. */ @JsonProperty("finalResult") ToolResultExpanded finalResult diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowJournalPutParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowJournalPutParams.java index 890c69647e..6cd69c5476 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowJournalPutParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowJournalPutParams.java @@ -33,6 +33,7 @@ public record SessionWorkflowJournalPutParams( /** Namespaced journal key. */ @JsonProperty("key") String key, /** JSON result to memoize. */ + @JsonInclude(JsonInclude.Include.ALWAYS) @JsonProperty("resultJson") Object resultJson ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowRunFromToolParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowRunFromToolParams.java index d68976fb76..78b199562e 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowRunFromToolParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowRunFromToolParams.java @@ -29,6 +29,7 @@ record SessionWorkflowRunFromToolParams( /** Registered workflow name. */ @JsonProperty("name") String name, /** Workflow input value. */ + @JsonInclude(JsonInclude.Include.ALWAYS) @JsonProperty("args") Object args, /** Tool-originated workflow invocation options. */ @JsonProperty("options") WorkflowToolRunOptions options, diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowRunParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowRunParams.java index 642e2f7fcf..84883913ff 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowRunParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowRunParams.java @@ -29,6 +29,7 @@ public record SessionWorkflowRunParams( /** Registered workflow name. */ @JsonProperty("name") String name, /** Workflow input value. */ + @JsonInclude(JsonInclude.Include.ALWAYS) @JsonProperty("args") Object args, /** Workflow invocation options. */ @JsonProperty("options") WorkflowRunOptions options diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SettingsEditOperation.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SettingsEditOperation.java new file mode 100644 index 0000000000..ff6d7a5c69 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SettingsEditOperation.java @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * One atomic settings mutation. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +enum SettingsEditOperation { + /** The {@code set} variant. */ + SET("set"), + /** The {@code add-item} variant. */ + ADD_ITEM("add-item"), + /** The {@code remove-item} variant. */ + REMOVE_ITEM("remove-item"), + /** The {@code remove} variant. */ + REMOVE("remove"); + + private final String value; + SettingsEditOperation(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static SettingsEditOperation fromValue(String value) { + for (SettingsEditOperation v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown SettingsEditOperation value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SettingsEditResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SettingsEditResult.java new file mode 100644 index 0000000000..ae05fb4237 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SettingsEditResult.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Outcome of an atomic user or repository settings edit. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SettingsEditResult( + /** Changed, unchanged or invalid existing file. Rejected input, validation failures and I/O errors remain RPC errors. */ + @JsonProperty("status") SettingsEditStatus status +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SettingsEditStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SettingsEditStatus.java new file mode 100644 index 0000000000..4bf228fc66 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SettingsEditStatus.java @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Whether one atomic settings edit changed a file. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +enum SettingsEditStatus { + /** The {@code changed} variant. */ + CHANGED("changed"), + /** The {@code unchanged} variant. */ + UNCHANGED("unchanged"), + /** The {@code invalid} variant. */ + INVALID("invalid"); + + private final String value; + SettingsEditStatus(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static SettingsEditStatus fromValue(String value) { + for (SettingsEditStatus v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown SettingsEditStatus value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillsDiscoverParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillsDiscoverParams.java index 03b9d85c1b..5dbde0dfd8 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillsDiscoverParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillsDiscoverParams.java @@ -27,7 +27,7 @@ public record SkillsDiscoverParams( /** Optional list of project directory paths to scan for project-scoped skills */ @JsonProperty("projectPaths") List projectPaths, - /** Optional list of additional skill directory paths to include */ + /** Explicit discovery-only skill roots, not filesystem grants. Available without ambient discovery, subject to skill/managed disables. Relative paths resolve against the request's effective working directory when supplied. Null and omission are equivalent; an empty array contributes no explicit roots. */ @JsonProperty("skillDirectories") List skillDirectories, /** Optional skill scan paths to exclude from discovery. */ @JsonProperty("ignoredSkillsLocations") List ignoredSkillsLocations, diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillsGetDiscoveryPathsRequest.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillsGetDiscoveryPathsRequest.java new file mode 100644 index 0000000000..9b58939685 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillsGetDiscoveryPathsRequest.java @@ -0,0 +1,131 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import java.util.Objects; +import javax.annotation.processing.Generated; + +/** + * Optional project paths to enumerate. + *

+ * Required inputs are constructor arguments. Optional inputs have fluent setters. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +public final class SkillsGetDiscoveryPathsRequest { + + /** Optional list of project directory paths. When omitted or empty, only personal and custom directories are returned. */ + @JsonProperty("projectPaths") + private List projectPaths; + + /** Optional skill scan paths to exclude from discovery. */ + @JsonProperty("ignoredSkillsLocations") + private List ignoredSkillsLocations; + + /** When true, omit the host's personal and custom skill directories, leaving only project directories. For multitenant deployments. */ + @JsonProperty("excludeHostSkills") + private Boolean excludeHostSkills; + + /** Explicit discovery-only skill roots to enumerate, including configured missing directories. Relative paths resolve against the request's effective working directory when supplied. These are not filesystem grants or preferred creation targets. Null and omission are equivalent. */ + @JsonProperty("skillDirectories") + private List skillDirectories; + + /** + * Creates a request with its required inputs. + */ + public SkillsGetDiscoveryPathsRequest() { + } + + /** + * Returns the {@code projectPaths} property. + * + * @return Optional list of project directory paths. When omitted or empty, only personal and custom directories are returned. + */ + public List getProjectPaths() { + return projectPaths; + } + + /** + * Returns the {@code ignoredSkillsLocations} property. + * + * @return Optional skill scan paths to exclude from discovery. + */ + public List getIgnoredSkillsLocations() { + return ignoredSkillsLocations; + } + + /** + * Returns the {@code excludeHostSkills} property. + * + * @return When true, omit the host's personal and custom skill directories, leaving only project directories. For multitenant deployments. + */ + public Boolean getExcludeHostSkills() { + return excludeHostSkills; + } + + /** + * Returns the {@code skillDirectories} property. + * + * @return Explicit discovery-only skill roots to enumerate, including configured missing directories. Relative paths resolve against the request's effective working directory when supplied. These are not filesystem grants or preferred creation targets. Null and omission are equivalent. + */ + public List getSkillDirectories() { + return skillDirectories; + } + + /** + * Sets the {@code projectPaths} property. + * + * @param value Optional list of project directory paths. When omitted or empty, only personal and custom directories are returned. + * @return this request + */ + public SkillsGetDiscoveryPathsRequest setProjectPaths(List value) { + this.projectPaths = value; + return this; + } + + /** + * Sets the {@code ignoredSkillsLocations} property. + * + * @param value Optional skill scan paths to exclude from discovery. + * @return this request + */ + public SkillsGetDiscoveryPathsRequest setIgnoredSkillsLocations(List value) { + this.ignoredSkillsLocations = value; + return this; + } + + /** + * Sets the {@code excludeHostSkills} property. + * + * @param value When true, omit the host's personal and custom skill directories, leaving only project directories. For multitenant deployments. + * @return this request + */ + public SkillsGetDiscoveryPathsRequest setExcludeHostSkills(Boolean value) { + this.excludeHostSkills = value; + return this; + } + + /** + * Sets the {@code skillDirectories} property. + * + * @param value Explicit discovery-only skill roots to enumerate, including configured missing directories. Relative paths resolve against the request's effective working directory when supplied. These are not filesystem grants or preferred creation targets. Null and omission are equivalent. + * @return this request + */ + public SkillsGetDiscoveryPathsRequest setSkillDirectories(List value) { + this.skillDirectories = value; + return this; + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/StaffLogLevelMigrationStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/StaffLogLevelMigrationStatus.java new file mode 100644 index 0000000000..1538068712 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/StaffLogLevelMigrationStatus.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * What the one-time staff log-level migration did. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +enum StaffLogLevelMigrationStatus { + /** The {@code ineligible} variant. */ + INELIGIBLE("ineligible"), + /** The {@code current} variant. */ + CURRENT("current"), + /** The {@code refreshed} variant. */ + REFRESHED("refreshed"); + + private final String value; + StaffLogLevelMigrationStatus(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static StaffLogLevelMigrationStatus fromValue(String value) { + for (StaffLogLevelMigrationStatus v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown StaffLogLevelMigrationStatus value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/StaffUpdateChannelMigrationStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/StaffUpdateChannelMigrationStatus.java new file mode 100644 index 0000000000..a3cb8f83d7 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/StaffUpdateChannelMigrationStatus.java @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * What the one-time staff update-channel migration did. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +enum StaffUpdateChannelMigrationStatus { + /** The {@code ineligible} variant. */ + INELIGIBLE("ineligible"), + /** The {@code current} variant. */ + CURRENT("current"), + /** The {@code migrated} variant. */ + MIGRATED("migrated"), + /** The {@code completed} variant. */ + COMPLETED("completed"), + /** The {@code excluded-experimental} variant. */ + EXCLUDED_EXPERIMENTAL("excluded-experimental"); + + private final String value; + StaffUpdateChannelMigrationStatus(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static StaffUpdateChannelMigrationStatus fromValue(String value) { + for (StaffUpdateChannelMigrationStatus v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown StaffUpdateChannelMigrationStatus value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsApplyTopKeyParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsApplyTopKeyParams.java new file mode 100644 index 0000000000..c81c4d218b --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsApplyTopKeyParams.java @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * A single top-level setting subtree to validate and write. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsApplyTopKeyParams( + /** Canonical path of the setting the caller is editing. Currently informational only. */ + @JsonProperty("canonicalPath") List canonicalPath, + /** Top-level setting name to write. */ + @JsonProperty("topKey") String topKey, + /** Value to write for the whole top-level key. Omitted when `delete` is true. Explicit null is preserved and validated as null. */ + @JsonProperty("value") Object value, + /** True to remove the top-level key instead of writing it. */ + @JsonProperty("delete") Boolean delete, + /** Records that the user chose an update channel explicitly, so the one-time staff channel migration stops reapplying. Honored only for the `autoUpdatesChannel` key. */ + @JsonProperty("completeStaffUpdateChannelMigration") Boolean completeStaffUpdateChannelMigration +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsApplyTopKeyResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsApplyTopKeyResult.java new file mode 100644 index 0000000000..85bdfd1d1a --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsApplyTopKeyResult.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Outcome of validating and writing one top-level setting subtree. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsApplyTopKeyResult( + /** `ok` when the value was written, `validation-error` when it was rejected before any write, `write-error` when validation passed but the write failed. */ + @JsonProperty("status") String status, + /** Problems found, present only when `status` is `validation-error`. */ + @JsonProperty("issues") List issues, + /** Why the write failed, present only when `status` is `write-error`. */ + @JsonProperty("error") String error +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsBuildRuntimeParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsBuildRuntimeParams.java new file mode 100644 index 0000000000..7b95e8df04 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsBuildRuntimeParams.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * The overrides and host context to resolve effective runtime settings from. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsBuildRuntimeParams( + /** Settings that take precedence over every document and environment layer, such as the values a host derived from its own command line. Keyed by setting name, with each value carried verbatim, so a host may pass keys this runtime does not recognize. Absent when the host contributes no overrides of its own. */ + @JsonProperty("overrides") Map overrides, + /** The directory the settings are being resolved for. This selects the repository-scoped document, so a host resolving settings for a directory other than its own process working directory must pass that directory here. */ + @JsonProperty("workingDirectory") String workingDirectory, + /** The environment to read settings-bearing variables from. Passed explicitly rather than read from the server process so a host can resolve settings for an environment it is about to create rather than the one it runs in. */ + @JsonProperty("environment") Map environment, + /** The platform to apply platform-specific defaults for, in the same spelling the host's own runtime reports. */ + @JsonProperty("platform") String platform +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsDiagnoseProblemsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsDiagnoseProblemsParams.java new file mode 100644 index 0000000000..de7ccae6c7 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsDiagnoseProblemsParams.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * The settings layers to diagnose. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsDiagnoseProblemsParams( + /** Layers to diagnose, in the precedence order the caller applies them. */ + @JsonProperty("sources") List sources +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsEditParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsEditParams.java new file mode 100644 index 0000000000..24f1cbd7f4 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsEditParams.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * One atomic edit of a canonical setting in settings.json. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsEditParams( + /** Configuration directory to edit. Omitted or empty resolves the server's own home, as in `user.settings.loadForConfigDir`. */ + @JsonProperty("configDir") String configDir, + /** Nonempty canonical path segments of the setting to edit, starting with its top-level key. */ + @JsonProperty("canonicalPath") List canonicalPath, + /** Set a scalar, add or remove a string list item, or remove the setting. The handler rejects a missing or incompatible value. */ + @JsonProperty("operation") SettingsEditOperation operation, + /** Required boolean, string or number for set, required string for add-item and remove-item, and forbidden for remove. The runtime rejects all other JSON values. */ + @JsonProperty("value") Object value +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGetPathsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGetPathsParams.java new file mode 100644 index 0000000000..d9fca8e81a --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGetPathsParams.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Selects which settings file paths to resolve. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsGetPathsParams( + /** Absolute path of a repository root. Supply it to also resolve that repository's two settings file paths; omit it to resolve only the user path. */ + @JsonProperty("repoRoot") String repoRoot +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGetPathsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGetPathsResult.java new file mode 100644 index 0000000000..2ed0ae2d5e --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGetPathsResult.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * On-disk locations of the settings files this runtime reads. A path is reported whether or not the file exists. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsGetPathsResult( + /** The user settings file: `settings.json` under the resolved Copilot home. This is the file `user.settings.get`, `user.settings.load`, and `user.settings.set` read and write. */ + @JsonProperty("userSettings") String userSettings, + /** The repository's checked-in settings file, or null when no repository root was supplied. */ + @JsonProperty("repoSettings") String repoSettings, + /** The repository's local, untracked settings file, or null when no repository root was supplied. */ + @JsonProperty("repoLocalSettings") String repoLocalSettings +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGetUnknownKeysWarningParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGetUnknownKeysWarningParams.java new file mode 100644 index 0000000000..a90c4a6853 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGetUnknownKeysWarningParams.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Selects the configuration directory whose user settings document should be inspected for unrecognized top-level keys. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsGetUnknownKeysWarningParams( + /** The configuration directory, home directory and environment to resolve the settings document from. A host that scopes a session to its own Copilot home must pass that directory here, or the server's own home is inspected instead. */ + @JsonProperty("context") UserSettingsHostContext context +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGovernanceKeysResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGovernanceKeysResult.java new file mode 100644 index 0000000000..d4c0891e8f --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGovernanceKeysResult.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Top-level user-setting keys grouped by who may govern them. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsGovernanceKeysResult( + /** Top-level keys a repository may override through `.github/copilot/settings.json` or `.github/copilot/settings.local.json`. */ + @JsonProperty("repo") List repo, + /** Top-level keys a repository may override through a Claude-compatible repository config. */ + @JsonProperty("claudeRepo") List claudeRepo, + /** Top-level keys an enterprise policy may fix through managed settings. */ + @JsonProperty("managed") List managed +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsHostContext.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsHostContext.java new file mode 100644 index 0000000000..441023ed14 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsHostContext.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * The filesystem and environment facts a user-settings file is resolved from. Passed explicitly rather than read from the runtime's own process, so a host whose configuration directory differs from its process environment resolves the file it means. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsHostContext( + /** The Copilot configuration directory, when the host set one. Takes precedence over `COPILOT_HOME` and the home directory. */ + @JsonProperty("configDir") String configDir, + /** The user's home directory, used when neither a configuration directory nor `COPILOT_HOME` is set. */ + @JsonProperty("homeDirectory") String homeDirectory, + /** The host's environment variables. `COPILOT_HOME` is read from here, as are the variables a settings hook may expand. */ + @JsonProperty("environment") Map environment +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsLoadAndMergeRepoParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsLoadAndMergeRepoParams.java new file mode 100644 index 0000000000..56af230edf --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsLoadAndMergeRepoParams.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * A repository to read settings from, and the user layer they are merged over. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsLoadAndMergeRepoParams( + /** Absolute path of the repository root whose settings to read. When this is the user's home directory, the Claude-compatible configuration is skipped, so a home-directory session does not inherit a personal config as if it were repository policy. */ + @JsonProperty("repoRoot") String repoRoot, + /** The user's own settings, keyed by setting name. The repository layer is merged over this; a repository may only add to the collection settings it is allowed to influence, so a user setting it does not name survives unchanged. */ + @JsonProperty("userSettings") Map userSettings +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsLoadAndMergeRepoResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsLoadAndMergeRepoResult.java new file mode 100644 index 0000000000..15b319f3d9 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsLoadAndMergeRepoResult.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * The settings a consumer should act on inside a repository, together with any diagnostic raised while reading the repository layer. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsLoadAndMergeRepoResult( + /** The merged settings document, keyed by setting name. Equals the supplied user layer when the repository declares no settings. */ + @JsonProperty("settings") Map settings, + /** Why part of the repository document was ignored, as a single human-readable string joining every diagnostic with `; `. Null when it loaded cleanly. The settings that did load are still merged, so a consumer should surface this and continue rather than treat it as a failure. */ + @JsonProperty("warning") String warning +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsLoadForConfigDirParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsLoadForConfigDirParams.java new file mode 100644 index 0000000000..0f745e7002 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsLoadForConfigDirParams.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Selects the configuration directory whose user settings to read. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsLoadForConfigDirParams( + /** Copilot configuration directory to read the settings document from, taking precedence over the server's own `COPILOT_HOME` and default home. Omit it, or pass an empty string, to read the directory the server resolved for itself. */ + @JsonProperty("configDir") String configDir +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsLoadForEditResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsLoadForEditResult.java new file mode 100644 index 0000000000..0a5b05d3c8 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsLoadForEditResult.java @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Contents of settings.json as stored, for a caller about to rewrite it. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsLoadForEditResult( + /** `ok` when the file parsed, `missing` when there is no file yet, `invalid` when the file exists but will not parse. */ + @JsonProperty("status") String status, + /** Parsed contents, present only when `status` is `ok`. */ + @JsonProperty("settings") Object settings +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsLoadRepoParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsLoadRepoParams.java new file mode 100644 index 0000000000..2f809ae5b6 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsLoadRepoParams.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Selects the repository whose checked-in settings to read. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsLoadRepoParams( + /** Absolute path of the repository root whose settings to read. When this is the user's home directory, the Claude-compatible configuration is skipped, so a home-directory session does not inherit a personal config as if it were repository policy. */ + @JsonProperty("repoRoot") String repoRoot +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsLoadRepoResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsLoadRepoResult.java new file mode 100644 index 0000000000..eb3f16cb85 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsLoadRepoResult.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * A repository's settings as stored, together with any diagnostic raised while reading them. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsLoadRepoResult( + /** The repository's settings document, keyed by setting name, with each value carried verbatim. Null when the repository declares no settings. */ + @JsonProperty("settings") Map settings, + /** Why part of the document was ignored, as a single human-readable string joining every diagnostic with `; `. Null when the document loaded cleanly. The settings that did load are still reported, so a consumer should surface this and continue rather than treat it as a failure. */ + @JsonProperty("warning") String warning +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsLoadWithWarningResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsLoadWithWarningResult.java new file mode 100644 index 0000000000..1f9ba38d07 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsLoadWithWarningResult.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * The user's stored settings, together with any diagnostic raised while reading them. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsLoadWithWarningResult( + /** The stored user settings document, keyed by setting name, with each value carried verbatim. Empty when the file could not be read at all. */ + @JsonProperty("settings") Map settings, + /** Why part of the document was ignored, phrased for display to the user. Null when the document loaded cleanly. The settings that did load are still reported, so a consumer may surface this and continue rather than treat it as a failure. */ + @JsonProperty("warning") String warning +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMergeManagedAndUserParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMergeManagedAndUserParams.java new file mode 100644 index 0000000000..740de3bc35 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMergeManagedAndUserParams.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * The managed (enterprise policy) layer and the user layer to fold together. The field names carry the layer identity: nothing about a settings object identifies which layer produced it, so a generic base/override pair would let a caller transpose them and silently resolve the governance keys in the user's favour. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsMergeManagedAndUserParams( + /** The managed layer. Its governance keys win, and the per-entry `enabledPlugins` and `extraKnownMarketplaces` control maps are stamped back from it after the union. Omitted when the host is unmanaged. */ + @JsonProperty("managed") Object managed, + /** The user's own settings layer. Wins on every key the managed layer does not govern. Omitted when the user has no settings yet. */ + @JsonProperty("user") Object user +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMergeManagedAndUserResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMergeManagedAndUserResult.java new file mode 100644 index 0000000000..c28bbddba3 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMergeManagedAndUserResult.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The user layer folded over the managed layer. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsMergeManagedAndUserResult( + /** The merged settings. An empty object when neither layer was supplied, so an unmanaged host with no user settings governs nothing. */ + @JsonProperty("settingsJson") Object settingsJson +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMergeRepoValueParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMergeRepoValueParams.java new file mode 100644 index 0000000000..30c47590c4 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMergeRepoValueParams.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * The user layer and the repository layer to fold together. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsMergeRepoValueParams( + /** The user's settings, already resolved against any managed layer. */ + @JsonInclude(JsonInclude.Include.ALWAYS) + @JsonProperty("userSettings") Object userSettings, + /** The repository's checked-in settings layer. */ + @JsonInclude(JsonInclude.Include.ALWAYS) + @JsonProperty("repoSettings") Object repoSettings +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMergeRepoValueResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMergeRepoValueResult.java new file mode 100644 index 0000000000..136bf12f30 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMergeRepoValueResult.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The repository layer folded into the user layer. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsMergeRepoValueResult( + /** The merged settings, with the per-entry managed control maps re-stamped from the runtime's ambient managed policy. */ + @JsonProperty("settingsJson") Object settingsJson +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMigrateObsoleteOnDiskParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMigrateObsoleteOnDiskParams.java new file mode 100644 index 0000000000..8c7fce5dd4 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMigrateObsoleteOnDiskParams.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Selects the configuration directory whose settings document to clean. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsMigrateObsoleteOnDiskParams( + /** The configuration directory holding the settings document to clean. A host that scopes a session to its own Copilot home passes that directory here; omit it to clean the document under the home the server resolved for itself. The home directory and environment are deliberately not caller-supplied: they decide only the settings path root and the two hook-parsing flags, and those describe the process doing the reading rather than the caller. */ + @JsonProperty("configDir") String configDir +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMigrateObsoleteOnDiskResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMigrateObsoleteOnDiskResult.java new file mode 100644 index 0000000000..5e894430ed --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMigrateObsoleteOnDiskResult.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The outcome of the one-time obsolete-settings cleanup. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsMigrateObsoleteOnDiskResult( + /** What the cleanup did. Only `changed` rewrote the document. The cleaned settings are not returned here: the document is written before this replies, so `user.settings.load` reads it authoritatively. */ + @JsonProperty("status") UserSettingsMigrateObsoleteOnDiskStatus status +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMigrateObsoleteOnDiskStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMigrateObsoleteOnDiskStatus.java new file mode 100644 index 0000000000..38ef9d9a42 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMigrateObsoleteOnDiskStatus.java @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * What the one-time obsolete-settings cleanup did to the document. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +enum UserSettingsMigrateObsoleteOnDiskStatus { + /** The {@code missing} variant. */ + MISSING("missing"), + /** The {@code invalid} variant. */ + INVALID("invalid"), + /** The {@code unchanged} variant. */ + UNCHANGED("unchanged"), + /** The {@code changed} variant. */ + CHANGED("changed"); + + private final String value; + UserSettingsMigrateObsoleteOnDiskStatus(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static UserSettingsMigrateObsoleteOnDiskStatus fromValue(String value) { + for (UserSettingsMigrateObsoleteOnDiskStatus v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown UserSettingsMigrateObsoleteOnDiskStatus value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMigrateStaffLogLevelOnceParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMigrateStaffLogLevelOnceParams.java new file mode 100644 index 0000000000..24220b8882 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMigrateStaffLogLevelOnceParams.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Selects the configuration directory to run the one-time staff log-level migration against, and supplies the caller's record of whether it has already run. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsMigrateStaffLogLevelOnceParams( + /** The configuration directory, home directory and environment to resolve the settings and global-state documents from. A host that scopes a session to its own Copilot home must pass that directory here, or the migration is evaluated against the server's own home instead. */ + @JsonProperty("context") UserSettingsHostContext context, + /** When the caller last recorded this migration, as an ISO-8601 timestamp. Supplying it lets the runtime answer `current` without re-running the evaluation. */ + @JsonProperty("cachedCompletedAt") String cachedCompletedAt +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMigrateStaffLogLevelOnceResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMigrateStaffLogLevelOnceResult.java new file mode 100644 index 0000000000..2ffc6ba552 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMigrateStaffLogLevelOnceResult.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * The outcome of the one-time staff log-level migration. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsMigrateStaffLogLevelOnceResult( + /** What the migration did. Only `refreshed` can have changed the persisted settings. */ + @JsonProperty("status") StaffLogLevelMigrationStatus status, + /** When the migration was recorded as done, as an ISO-8601 timestamp. Absent only for `ineligible`, which records nothing. */ + @JsonProperty("completedAt") String completedAt, + /** The settings document as it stands after the migration, keyed by setting name. Present only for `refreshed`, and only when the document could be re-read cleanly. */ + @JsonProperty("settings") Map settings +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMigrateStaffUpdateChannelOnceParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMigrateStaffUpdateChannelOnceParams.java new file mode 100644 index 0000000000..77a3287abe --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMigrateStaffUpdateChannelOnceParams.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * The filesystem and environment facts a user-settings file is resolved from. Passed explicitly rather than read from the runtime's own process, so a host whose configuration directory differs from its process environment resolves the file it means. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsMigrateStaffUpdateChannelOnceParams( + /** The Copilot configuration directory, when the host set one. Takes precedence over `COPILOT_HOME` and the home directory. */ + @JsonProperty("configDir") String configDir, + /** The user's home directory, used when neither a configuration directory nor `COPILOT_HOME` is set. */ + @JsonProperty("homeDirectory") String homeDirectory, + /** The host's environment variables. `COPILOT_HOME` is read from here, as are the variables a settings hook may expand. */ + @JsonProperty("environment") Map environment +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMigrateStaffUpdateChannelOnceResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMigrateStaffUpdateChannelOnceResult.java new file mode 100644 index 0000000000..8d21eca4f1 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsMigrateStaffUpdateChannelOnceResult.java @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The outcome of the one-time staff update-channel migration. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsMigrateStaffUpdateChannelOnceResult( + /** What the migration did. Only `migrated` means the persisted settings changed. */ + @JsonProperty("status") StaffUpdateChannelMigrationStatus status, + /** When the migration was recorded as done, as an ISO-8601 timestamp. Absent only for `ineligible`, which records nothing. */ + @JsonProperty("completedAt") String completedAt +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsNormalizeParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsNormalizeParams.java new file mode 100644 index 0000000000..f43d774226 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsNormalizeParams.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Settings object whose key names should be canonicalized. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsNormalizeParams( + /** Free-form settings object, possibly using legacy or snake_case key names. */ + @JsonInclude(JsonInclude.Include.ALWAYS) + @JsonProperty("settings") Object settings +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsNormalizeResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsNormalizeResult.java new file mode 100644 index 0000000000..badb433c2a --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsNormalizeResult.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Settings object with canonical key names. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsNormalizeResult( + /** The same settings with every legacy or snake_case key renamed to its canonical spelling. */ + @JsonProperty("settings") Object settings +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsPathResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsPathResult.java new file mode 100644 index 0000000000..5c4ac0d88e --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsPathResult.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The on-disk location of the user's settings document. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsPathResult( + /** Absolute path to the settings file this runtime reads and writes. Normally `settings.json` under the Copilot configuration directory; an extensionless legacy `settings` file is reported instead when it exists and `settings.json` does not. */ + @JsonProperty("path") String path +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsProblem.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsProblem.java new file mode 100644 index 0000000000..057a5ac6a5 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsProblem.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * One setting that will not take effect, and why. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsProblem( + /** Stable identifier for this problem within the reported set, so a caller can track a selection across refreshes. */ + @JsonProperty("id") String id, + /** What is wrong in machine-readable form, for a caller that wants to branch rather than display. */ + @JsonProperty("kind") String kind, + /** Coarse grouping for the problem, for a caller that presents problems in sections. */ + @JsonProperty("category") String category, + /** The settings layer the problem was found in. */ + @JsonProperty("source") UserSettingsProblemLocation source, + /** The offending key as written in the file, when the problem is attributable to a single key. Absent when the whole file is at fault. */ + @JsonProperty("rawKey") String rawKey, + /** Why the setting will not take effect. Never contains the setting's value, which may be a token or another secret. */ + @JsonProperty("explanation") String explanation +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsProblemLocation.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsProblemLocation.java new file mode 100644 index 0000000000..454408eb8b --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsProblemLocation.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The settings layer a problem was found in. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsProblemLocation( + /** Which layer this is: `user`, `repo`, or `local`. */ + @JsonProperty("kind") String kind, + /** The caller-supplied display label for the layer. */ + @JsonProperty("label") String label, + /** Absolute path of the file backing this layer. */ + @JsonProperty("filePath") String filePath +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsProblemSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsProblemSource.java new file mode 100644 index 0000000000..1c471916c5 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsProblemSource.java @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * One already-loaded settings layer to diagnose. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsProblemSource( + /** Which layer this is: `user`, `repo`, or `local`. */ + @JsonProperty("kind") String kind, + /** Caller-chosen display label for the layer, echoed back on each problem so a caller can group by layer without re-deriving its own naming. */ + @JsonProperty("label") String label, + /** Absolute path of the file backing this layer. */ + @JsonProperty("filePath") String filePath, + /** The layer's settings exactly as loaded, unnormalized. Empty when the file is missing or failed to parse. */ + @JsonProperty("config") Map config, + /** True when the file itself failed to parse, so its keys could not be read at all. */ + @JsonProperty("invalid") Boolean invalid +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsProblemsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsProblemsResult.java new file mode 100644 index 0000000000..6a5dcf5728 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsProblemsResult.java @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * The settings that will not take effect across the given layers. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsProblemsResult( + /** Each setting that will not take effect, and why. Empty when every layer is healthy. */ + @JsonProperty("problems") List problems +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsResetStaffModelSelectionOnceParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsResetStaffModelSelectionOnceParams.java new file mode 100644 index 0000000000..98ec0b7fb3 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsResetStaffModelSelectionOnceParams.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Selects the configuration directory to run the one-time staff model-selection reset against, and supplies the caller's record of whether it has already run. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsResetStaffModelSelectionOnceParams( + /** The configuration directory, home directory and environment to resolve the settings and global-state documents from. A host that scopes a session to its own Copilot home must pass that directory here, or the reset is evaluated against the server's own home instead. */ + @JsonProperty("context") UserSettingsHostContext context, + /** Whether the signed-in account is a staff account. The reset applies only to staff, so anything other than `true` returns `didReset: false` without touching the settings document. */ + @JsonProperty("staff") Boolean staff, + /** When the caller last recorded this reset, as an ISO-8601 timestamp. A timestamp at or after the cutoff means the reset already ran and is returned unchanged. */ + @JsonProperty("staffModelResetAt") String staffModelResetAt +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsResetStaffModelSelectionOnceResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsResetStaffModelSelectionOnceResult.java new file mode 100644 index 0000000000..90f9668f4f --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsResetStaffModelSelectionOnceResult.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * The outcome of the one-time staff model-selection reset. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsResetStaffModelSelectionOnceResult( + /** Whether the persisted model selection was cleared. Only `true` means the settings document changed and the host should adopt the returned settings. */ + @JsonProperty("didReset") Boolean didReset, + /** The timestamp the host should now record, as an ISO-8601 timestamp. Carries the caller's own value back unchanged when nothing was reset. */ + @JsonProperty("resetAt") String resetAt, + /** The cutoff this reset is gated on, as an ISO-8601 timestamp. A recorded timestamp at or after it means the reset is already done. */ + @JsonProperty("cutoff") String cutoff, + /** The settings document as it stands after the reset, keyed by setting name. Present only when `didReset` is true. */ + @JsonProperty("settings") Map settings +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetHookDisabledParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetHookDisabledParams.java new file mode 100644 index 0000000000..e8541c2841 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetHookDisabledParams.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * One hook to disable or re-enable. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsSetHookDisabledParams( + /** Path of the settings.json to rewrite. */ + @JsonProperty("settingsPath") String settingsPath, + /** Durable content-hash key of the hook, as `hooks.loadResources` reports it. */ + @JsonProperty("disableKey") String disableKey, + /** True adds the key to `disabledHooks`, false removes it. */ + @JsonProperty("disabled") Boolean disabled +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetHookDisabledResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetHookDisabledResult.java new file mode 100644 index 0000000000..5548816405 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetHookDisabledResult.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Outcome of rewriting the `disabledHooks` setting. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsSetHookDisabledResult( + /** `ok` when the setting was written, `invalid` when settings.json could not be parsed and was left alone, `error` when the write itself failed. */ + @JsonProperty("status") String status, + /** Why the write failed. Absent when the status is `ok`. */ + @JsonProperty("errorMessage") String errorMessage +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetParams.java index ba19886c24..d56ecaa225 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetParams.java @@ -25,6 +25,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) public record UserSettingsSetParams( /** Partial user settings to write, as a free-form object keyed by setting name */ + @JsonInclude(JsonInclude.Include.ALWAYS) @JsonProperty("settings") Object settings ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsUnknownKeysWarningResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsUnknownKeysWarningResult.java new file mode 100644 index 0000000000..404b8c57c0 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsUnknownKeysWarningResult.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The unrecognized top-level keys found in the user settings document, already phrased for display. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsUnknownKeysWarningResult( + /** The warning to show the user, naming the unrecognized keys and the file they were found in. Absent when every top-level key is recognized, and also when the document is missing or could not be read, because an unknown-key list taken from a document the runtime could not load would name keys that may not be there. */ + @JsonProperty("warning") String warning +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsUpdateParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsUpdateParams.java new file mode 100644 index 0000000000..9aed7c14b3 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsUpdateParams.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Names the user setting to merge into, and the partial object to merge. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsUpdateParams( + /** Top-level user-setting key to merge into, such as `extensions`. */ + @JsonProperty("key") String key, + /** Value to store for `key`. When both the stored value and this value are objects, their fields are shallow-merged and fields this object does not name are preserved. Otherwise this value replaces the stored value. */ + @JsonInclude(JsonInclude.Include.ALWAYS) + @JsonProperty("value") Object value, + /** Copilot configuration directory to write the settings document in, taking precedence over the server's own `COPILOT_HOME` and default home. Omit it, or pass an empty string, to use the directory the server resolved for itself. */ + @JsonProperty("configDir") String configDir, + /** Single-component name for the settings document at `/settings/.json`; path separators, `.` and `..` are rejected. Omit it, or pass an empty string, to use the main settings document. */ + @JsonProperty("subDir") String subDir +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsUpdateResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsUpdateResult.java new file mode 100644 index 0000000000..2d13157a3b --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsUpdateResult.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * The stored value of the user setting after the merge. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsUpdateResult( + /** The key's stored value after the write: the merged object when both the stored value and the supplied value are objects, otherwise the supplied value. */ + @JsonProperty("value") Object value +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsValidateParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsValidateParams.java new file mode 100644 index 0000000000..3e69f4b138 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsValidateParams.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * A single top-level setting subtree to validate. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsValidateParams( + /** Top-level setting name to validate. */ + @JsonProperty("topKey") String topKey, + /** Value to validate. Omitted when `delete` is true. */ + @JsonProperty("value") Object value, + /** True to validate a removal of this key. The current settings file is not read. */ + @JsonProperty("delete") Boolean delete +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsValidateResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsValidateResult.java new file mode 100644 index 0000000000..730fb00195 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsValidateResult.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Outcome of validating one top-level setting subtree. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsValidateResult( + /** True when the subtree is valid. */ + @JsonProperty("ok") Boolean ok, + /** Problems found, empty when `ok` is true. */ + @JsonProperty("issues") List issues +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsValidationIssue.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsValidationIssue.java new file mode 100644 index 0000000000..68404e53a7 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsValidationIssue.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * One problem found while validating a setting value. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsValidationIssue( + /** Where in the settings tree the problem sits, as object keys and array indices. The first element is the top-level key; a whole-value problem is reported as that single key. */ + @JsonProperty("path") List path, + /** Human-readable description of the problem. */ + @JsonProperty("message") String message +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsWriteAllParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsWriteAllParams.java new file mode 100644 index 0000000000..e5929a8289 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsWriteAllParams.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * The whole content to store in the user settings file. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record UserSettingsWriteAllParams( + /** The settings object to store. It replaces the file's entire content, so a key absent here is removed. */ + @JsonInclude(JsonInclude.Include.ALWAYS) + @JsonProperty("settings") Object settings +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkflowExecuteParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkflowExecuteParams.java index 5c53ad155f..e4001bb82e 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkflowExecuteParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkflowExecuteParams.java @@ -33,6 +33,7 @@ public record WorkflowExecuteParams( /** Opaque token identifying this workflow execution attempt. */ @JsonProperty("executionToken") String executionToken, /** Workflow input value. */ + @JsonInclude(JsonInclude.Include.ALWAYS) @JsonProperty("args") Object args ) { } diff --git a/java/sdk/src/main/java/com/github/copilot/CliServerManager.java b/java/sdk/src/main/java/com/github/copilot/CliServerManager.java index 92203ab755..0ccd2995e8 100644 --- a/java/sdk/src/main/java/com/github/copilot/CliServerManager.java +++ b/java/sdk/src/main/java/com/github/copilot/CliServerManager.java @@ -11,6 +11,8 @@ import java.net.Socket; import java.net.URI; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; @@ -353,9 +355,17 @@ private List resolveCliCommand(String cliPath, List args) { return result; } - // On Windows, use cmd /c to resolve the executable + // Own native executables directly so terminating the process cannot orphan + // a child behind cmd.exe. Batch shims still require the shell. String os = System.getProperty("os.name").toLowerCase(); if (os.contains("win") && !new File(cliPath).isAbsolute()) { + String executable = resolveWindowsExecutable(cliPath); + if (executable != null) { + var result = new ArrayList(); + result.add(executable); + result.addAll(args); + return result; + } var result = new ArrayList(); result.add("cmd"); result.add("/c"); @@ -370,6 +380,59 @@ private List resolveCliCommand(String cliPath, List args) { return result; } + private String resolveWindowsExecutable(String cliPath) { + Path cwd = Path.of(options.getCwd() != null ? options.getCwd() : System.getProperty("user.dir")) + .toAbsolutePath(); + var directories = new ArrayList(); + var environment = options.getEnvironment() != null ? options.getEnvironment() : System.getenv(); + boolean hasDirectory = cliPath.contains("\\") || cliPath.contains("/"); + if (hasDirectory || environment.keySet().stream() + .noneMatch(key -> key.equalsIgnoreCase("NoDefaultCurrentDirectoryInExePath"))) { + directories.add(cwd.toString()); + } + if (!hasDirectory) { + String pathValue = environment.entrySet().stream().filter(entry -> entry.getKey().equalsIgnoreCase("PATH")) + .map(java.util.Map.Entry::getValue).findFirst().orElse(""); + directories.addAll(Arrays.asList(pathValue.split(File.pathSeparator))); + } + String pathExtensions = environment.entrySet().stream() + .filter(entry -> entry.getKey().equalsIgnoreCase("PATHEXT")).map(java.util.Map.Entry::getValue) + .findFirst().orElse(".COM;.EXE;.BAT;.CMD"); + String[] names = new File(cliPath).getName().contains(".") + ? new String[]{cliPath} + : Arrays.stream(pathExtensions.split(";")).map(extension -> cliPath + extension).toArray(String[]::new); + // Preserve invalid-command diagnostics even when implicit cwd search is + // disabled. + if (names.length > 0) { + Path.of(names[0]); + } + for (String directory : directories) { + if (directory.isBlank()) { + continue; + } + if (directory.startsWith("\"") && directory.endsWith("\"")) { + directory = directory.substring(1, directory.length() - 1); + } + Path searchDirectory; + try { + searchDirectory = cwd.resolve(directory); + } catch (InvalidPathException error) { + LOG.log(Level.FINE, "Skipping invalid PATH entry while resolving CLI executable", error); + continue; + } + for (String name : names) { + Path candidate = searchDirectory.resolve(name); + if (Files.isRegularFile(candidate)) { + String lowerName = name.toLowerCase(java.util.Locale.ROOT); + return lowerName.endsWith(".exe") || lowerName.endsWith(".com") + ? candidate.toAbsolutePath().toString() + : null; + } + } + } + return null; + } + void configureProcessEnvironment(ProcessBuilder pb) { if (options.getEnvironment() != null) { pb.environment().clear(); diff --git a/java/sdk/src/main/java/com/github/copilot/CopilotClient.java b/java/sdk/src/main/java/com/github/copilot/CopilotClient.java index baccd4ad19..de6081b39d 100644 --- a/java/sdk/src/main/java/com/github/copilot/CopilotClient.java +++ b/java/sdk/src/main/java/com/github/copilot/CopilotClient.java @@ -488,8 +488,10 @@ void setInProcessTransportFactory(InProcessTransportFactory factory) { private static InProcessTransport openInProcessTransport(CopilotClientOptions options) throws IOException { FfiRuntimeHost host = new FfiRuntimeHost(); try { - Path explicitEntrypoint = NativeRuntimeLoader.resolveConfiguredEntrypoint(); - host.start(explicitEntrypoint == null ? null : explicitEntrypoint.toString(), options); + String integrationId = System.getenv("GITHUB_COPILOT_INTEGRATION_ID"); + integrationId = integrationId == null || integrationId.isBlank() ? null : integrationId.trim(); + Path cliEntrypoint = NativeRuntimeLoader.resolveConfiguredEntrypoint(); + host.start(cliEntrypoint == null ? null : cliEntrypoint.toString(), integrationId, options); } catch (RuntimeException | Error e) { host.close(); throw e; @@ -1440,6 +1442,9 @@ CompletableFuture updateSessionOptionsForMode(CopilotSession session, Bool null, // envValueMode null, // allowAllMcpServerInstructions null, // skillDirectories + null, // agentDirectories + null, // instructionDirectories + null, // hookDirectories null, // ignoredSkillsLocations patchSkills, // includedBuiltinSkills null, // disabledSkills diff --git a/java/sdk/src/main/java/com/github/copilot/SessionRequestBuilder.java b/java/sdk/src/main/java/com/github/copilot/SessionRequestBuilder.java index 8d77086746..680ff0c160 100644 --- a/java/sdk/src/main/java/com/github/copilot/SessionRequestBuilder.java +++ b/java/sdk/src/main/java/com/github/copilot/SessionRequestBuilder.java @@ -153,6 +153,7 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess } request.setModel(config.getModel()); + request.setName(config.getName()); request.setClientName(config.getClientName()); request.setReasoningEffort(config.getReasoningEffort()); request.setReasoningSummary(config.getReasoningSummary()); @@ -196,10 +197,13 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess request.setAgent(config.getAgent()); request.setInfiniteSessions(config.getInfiniteSessions()); request.setSkillDirectories(config.getSkillDirectories()); + request.setAgentDirectories(config.getAgentDirectories()); + request.setHookDirectories(config.getHookDirectories()); request.setInstructionDirectories(config.getInstructionDirectories()); request.setPluginDirectories(config.getPluginDirectories()); request.setLargeOutput(config.getLargeOutput()); request.setToolSearch(config.getToolSearch()); + request.setImageGeneration(config.getImageGeneration()); request.setMemory(config.getMemory()); request.setDisabledSkills(config.getDisabledSkills()); request.setDisabledMcpServers(config.getDisabledMcpServers()); @@ -362,10 +366,13 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo request.setDefaultAgent(config.getDefaultAgent()); request.setAgent(config.getAgent()); request.setSkillDirectories(config.getSkillDirectories()); + request.setAgentDirectories(config.getAgentDirectories()); + request.setHookDirectories(config.getHookDirectories()); request.setInstructionDirectories(config.getInstructionDirectories()); request.setPluginDirectories(config.getPluginDirectories()); request.setLargeOutput(config.getLargeOutput()); request.setToolSearch(config.getToolSearch()); + request.setImageGeneration(config.getImageGeneration()); request.setMemory(config.getMemory()); request.setDisabledSkills(config.getDisabledSkills()); request.setDisabledMcpServers(config.getDisabledMcpServers()); diff --git a/java/sdk/src/main/java/com/github/copilot/ffi/FfiRuntimeHost.java b/java/sdk/src/main/java/com/github/copilot/ffi/FfiRuntimeHost.java index e28a573bc8..825a44497a 100644 --- a/java/sdk/src/main/java/com/github/copilot/ffi/FfiRuntimeHost.java +++ b/java/sdk/src/main/java/com/github/copilot/ffi/FfiRuntimeHost.java @@ -98,6 +98,23 @@ private static Path resolveLibraryPath() throws IOException { * {@code env_json} */ public void start(String entrypointPath, CopilotClientOptions options) { + start(entrypointPath, null, options); + } + + /** + * Starts the in-process runtime and opens a connection. + * + * @param entrypointPath + * optional explicit legacy CLI entrypoint passed in + * {@code argv_json} + * @param cliLoginSyncIntegrationId + * optional integration identifier used to synchronize login state + * with the Copilot CLI credential store + * @param options + * client options used to construct {@code argv_json} and + * {@code env_json} + */ + public void start(String entrypointPath, String cliLoginSyncIntegrationId, CopilotClientOptions options) { Objects.requireNonNull(options, "options must not be null"); if (disposed.get()) { throw new IllegalStateException("FfiRuntimeHost is already closed."); @@ -106,7 +123,7 @@ public void start(String entrypointPath, CopilotClientOptions options) { throw new IllegalStateException("FfiRuntimeHost has already been started."); } - byte[] argvJson = buildArgvJson(entrypointPath, options); + byte[] argvJson = buildArgvJson(entrypointPath, cliLoginSyncIntegrationId, options); byte[] envJson = buildEnvJson(options); int hostHandle = runHostStartOnBlockingThread(argvJson, envJson); if (hostHandle == 0) { @@ -275,7 +292,7 @@ private int runHostStartOnBlockingThread(byte[] argvJson, byte[] envJson) { } } - private static byte[] buildArgvJson(String entrypointPath, CopilotClientOptions options) { + static byte[] buildArgvJson(String entrypointPath, String cliLoginSyncIntegrationId, CopilotClientOptions options) { List argv = new ArrayList<>(); if (entrypointPath != null) { if (entrypointPath.toLowerCase().endsWith(".js")) { @@ -312,6 +329,9 @@ private static byte[] buildArgvJson(String entrypointPath, CopilotClientOptions if (options.isRemote()) { argv.add("--remote"); } + if (cliLoginSyncIntegrationId != null) { + argv.add("--cli-login-sync-integration-id=" + cliLoginSyncIntegrationId); + } String[] cliArgs = options.getCliArgs(); if (cliArgs != null && cliArgs.length > 0) { diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/AgentInfo.java b/java/sdk/src/main/java/com/github/copilot/rpc/AgentInfo.java index 1f6f1688fe..96820af0ef 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/AgentInfo.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/AgentInfo.java @@ -4,6 +4,10 @@ package com.github.copilot.rpc; +import java.util.Collections; +import java.util.List; +import java.util.Map; + import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; @@ -27,6 +31,30 @@ public class AgentInfo { @JsonProperty("model") private String model; + @JsonProperty("hooks") + private Map>> hooks; + + /** + * Gets declarative hook actions executed only while this agent is active. + * + * @return actions keyed by event name, or {@code null} if unset + */ + public Map>> getHooks() { + return hooks == null ? null : Collections.unmodifiableMap(hooks); + } + + /** + * Sets declarative hook actions scoped to this agent. + * + * @param hooks + * actions keyed by event name + * @return this instance for chaining + */ + public AgentInfo setHooks(Map>> hooks) { + this.hooks = hooks; + return this; + } + /** * Gets the unique identifier of the agent. * diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java b/java/sdk/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java index d18de07fb1..69b2bcaf3d 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java @@ -34,6 +34,9 @@ public final class CreateSessionRequest { @JsonProperty("sessionId") private String sessionId; + @JsonProperty("name") + private String name; + @JsonProperty("clientName") private String clientName; @@ -147,6 +150,12 @@ public final class CreateSessionRequest { @JsonProperty("skillDirectories") private List skillDirectories; + @JsonProperty("agentDirectories") + private List agentDirectories; + + @JsonProperty("hookDirectories") + private List hookDirectories; + @JsonProperty("instructionDirectories") private List instructionDirectories; @@ -159,6 +168,9 @@ public final class CreateSessionRequest { @JsonProperty("toolSearch") private ToolSearchConfig toolSearch; + @JsonProperty("imageGeneration") + private ImageGenerationConfig imageGeneration; + @JsonProperty("memory") private MemoryConfiguration memory; @@ -288,6 +300,16 @@ public void setSessionId(String sessionId) { this.sessionId = sessionId; } + /** Gets the create-time session name. @return the session name */ + public String getName() { + return name; + } + + /** Sets the create-time session name. @param name the session name */ + public void setName(String name) { + this.name = name; + } + /** Gets the client name. @return the client name */ public String getClientName() { return clientName; @@ -720,6 +742,31 @@ public void setSkillDirectories(List skillDirectories) { this.skillDirectories = skillDirectories; } + /** Gets explicit agent discovery roots. @return the directories */ + public List getAgentDirectories() { + return agentDirectories == null ? null : Collections.unmodifiableList(agentDirectories); + } + + /** + * Sets explicit agent discovery roots. @param agentDirectories the directories + */ + public void setAgentDirectories(List agentDirectories) { + this.agentDirectories = agentDirectories; + } + + /** Gets explicit file-hook discovery roots. @return the directories */ + public List getHookDirectories() { + return hookDirectories == null ? null : Collections.unmodifiableList(hookDirectories); + } + + /** + * Sets explicit file-hook discovery roots. @param hookDirectories the + * directories + */ + public void setHookDirectories(List hookDirectories) { + this.hookDirectories = hookDirectories; + } + /** Gets instruction directories. @return the instruction directories */ public List getInstructionDirectories() { return instructionDirectories == null ? null : Collections.unmodifiableList(instructionDirectories); @@ -757,6 +804,19 @@ public ToolSearchConfig getToolSearch() { return toolSearch; } + /** Gets image generation config. @return the image generation config */ + public ImageGenerationConfig getImageGeneration() { + return imageGeneration; + } + + /** + * Sets image generation config. @param imageGeneration the image generation + * config + */ + public void setImageGeneration(ImageGenerationConfig imageGeneration) { + this.imageGeneration = imageGeneration; + } + /** Sets tool-search config. @param toolSearch the tool-search config */ public void setToolSearch(ToolSearchConfig toolSearch) { this.toolSearch = toolSearch; diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/CustomAgentConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/CustomAgentConfig.java index 62de19b6a0..9f9c428ab4 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/CustomAgentConfig.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/CustomAgentConfig.java @@ -36,6 +36,30 @@ @JsonInclude(JsonInclude.Include.NON_NULL) public class CustomAgentConfig { + @JsonProperty("hooks") + private Map>> hooks; + + /** + * Gets declarative hooks scoped to this agent. + * + * @return actions keyed by event name, or null when unset + */ + public Map>> getHooks() { + return hooks == null ? null : Collections.unmodifiableMap(hooks); + } + + /** + * Sets declarative hooks scoped to this agent. + * + * @param hooks + * actions keyed by event name + * @return this config for method chaining + */ + public CustomAgentConfig setHooks(Map>> hooks) { + this.hooks = hooks; + return this; + } + @JsonProperty("name") private String name; diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/ImageGenerationConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/ImageGenerationConfig.java new file mode 100644 index 0000000000..79b336b4da --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/rpc/ImageGenerationConfig.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +package com.github.copilot.rpc; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; + +/** + * Experimental. This API may change or be removed in a future + * release. Opt in to image generation through an authorized Copilot image + * model. Omission disables on create/cold resume and preserves resident resume + * state. Not persisted: re-supply after runtime restart. Policy, permissions, + * offline mode, and tool filters still apply. BYOK image generation is not + * supported. + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@CopilotExperimental +public class ImageGenerationConfig { + @JsonProperty("enabled") + private Boolean enabled; + + /** + * Gets the image generation opt-in. + * + * @return true to opt in, false to disable, or null when unset + */ + public Boolean getEnabled() { + return enabled; + } + + /** + * Enables or disables image generation. + * + * @param enabled + * the opt-in, or null when unset + * @return this config for method chaining + */ + public ImageGenerationConfig setEnabled(Boolean enabled) { + this.enabled = enabled; + return this; + } +} diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java index 927aa9555d..adb7d7a9cd 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java @@ -97,11 +97,14 @@ public class ResumeSessionConfig { private DefaultAgentConfig defaultAgent; private String agent; private List skillDirectories; + private List agentDirectories; + private List hookDirectories; private List includedBuiltinSkills; private List instructionDirectories; private List pluginDirectories; private LargeToolOutputConfig largeOutput; private ToolSearchConfig toolSearch; + private ImageGenerationConfig imageGeneration; private MemoryConfiguration memory; private List disabledSkills; private List disabledMcpServers; @@ -1176,7 +1179,9 @@ public ResumeSessionConfig clearEnableOnDemandInstructionDiscovery() { } /** - * Gets whether file-based hooks are enabled. + * Gets whether repository-origin file hooks from {@code .github/hooks/} and + * trusted repository-configured roots are enabled. Does not control explicit + * {@link #getHookDirectories()} or callback hooks. * * @return an {@link java.util.Optional} containing {@code true} to enable file * hooks or {@code false} to disable them, or @@ -1659,7 +1664,11 @@ public ResumeSessionConfig setAgent(String agent) { } /** - * Gets the skill directories. + * Gets explicit discovery-only skill roots, not general filesystem grants. + * Available without configuration discovery, subject to skill/managed disables. + * Relative paths resolve once when supplied against the request's effective + * working directory; retained roots do not move when it changes. Null and + * omission are equivalent; an empty list clears only the SDK contribution. * * @return the list of skill directory paths */ @@ -1679,6 +1688,59 @@ public ResumeSessionConfig setSkillDirectories(List skillDirectories) { return this; } + /** + * Gets explicit discovery-only agent roots, separate from custom agent objects. + * No general filesystem grant. Available without configuration discovery, + * subject to source/managed disables. Relative paths resolve once when supplied + * against the request's effective working directory; retained roots do not + * move. Null and omission are equivalent; an empty list clears only the SDK + * contribution. + * + * @return the agent directory paths + */ + public List getAgentDirectories() { + return agentDirectories == null ? null : Collections.unmodifiableList(agentDirectories); + } + + /** + * Sets explicit discovery-only agent roots. See {@link #getAgentDirectories()}. + * + * @param agentDirectories + * the agent directory paths + * @return this config for method chaining + */ + public ResumeSessionConfig setAgentDirectories(List agentDirectories) { + this.agentDirectories = agentDirectories == null ? null : new ArrayList<>(agentDirectories); + return this; + } + + /** + * Gets explicit discovery-only file-hook roots, separate from callback hooks. + * No general filesystem grant. Available without configuration discovery or + * file-hook enablement (repository-origin hooks only), subject to hook/managed + * disables. Relative paths resolve once when supplied against the request's + * effective working directory; retained roots do not move. Null and omission + * are equivalent; an empty list clears only the SDK contribution. + * + * @return the file-hook directory paths + */ + public List getHookDirectories() { + return hookDirectories == null ? null : Collections.unmodifiableList(hookDirectories); + } + + /** + * Sets explicit discovery-only file-hook roots. See + * {@link #getHookDirectories()}. + * + * @param hookDirectories + * the file-hook directory paths + * @return this config for method chaining + */ + public ResumeSessionConfig setHookDirectories(List hookDirectories) { + this.hookDirectories = hookDirectories == null ? null : new ArrayList<>(hookDirectories); + return this; + } + /** * Gets the runtime-bundled skill allowlist. * @@ -1702,7 +1764,12 @@ public ResumeSessionConfig setIncludedBuiltinSkills(List includedBuiltin } /** - * Gets the additional directories to search for custom instruction files. + * Gets explicit discovery-only instruction roots using existing SDK formats. No + * general filesystem grant. Available without configuration discovery, subject + * to instruction/managed disables. Relative paths resolve once when supplied + * against the request's effective working directory; retained roots do not + * move. Null and omission are equivalent; an empty list clears only the SDK + * contribution. * * @return the list of instruction directory paths */ @@ -1773,6 +1840,29 @@ public ToolSearchConfig getToolSearch() { return toolSearch; } + /** + * Gets the non-persisted image generation opt-in. + * + * @return the image generation config, or null when unset + */ + @CopilotExperimental + public ImageGenerationConfig getImageGeneration() { + return imageGeneration; + } + + /** + * Sets the image generation opt-in. Re-supply after runtime restart. + * + * @param imageGeneration + * the image generation config + * @return this config for method chaining + */ + @CopilotExperimental + public ResumeSessionConfig setImageGeneration(ImageGenerationConfig imageGeneration) { + this.imageGeneration = imageGeneration; + return this; + } + /** * Sets the tool-search configuration. * @@ -2311,6 +2401,8 @@ public ResumeSessionConfig clone() { copy.defaultAgent = this.defaultAgent; copy.agent = this.agent; copy.skillDirectories = this.skillDirectories != null ? new ArrayList<>(this.skillDirectories) : null; + copy.agentDirectories = this.agentDirectories != null ? new ArrayList<>(this.agentDirectories) : null; + copy.hookDirectories = this.hookDirectories != null ? new ArrayList<>(this.hookDirectories) : null; copy.includedBuiltinSkills = this.includedBuiltinSkills != null ? new ArrayList<>(this.includedBuiltinSkills) : null; @@ -2320,6 +2412,7 @@ public ResumeSessionConfig clone() { copy.pluginDirectories = this.pluginDirectories != null ? new ArrayList<>(this.pluginDirectories) : null; copy.largeOutput = this.largeOutput; copy.toolSearch = this.toolSearch; + copy.imageGeneration = this.imageGeneration; copy.memory = this.memory; copy.disabledSkills = this.disabledSkills != null ? new ArrayList<>(this.disabledSkills) : null; copy.disabledMcpServers = this.disabledMcpServers != null ? new ArrayList<>(this.disabledMcpServers) : null; diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java b/java/sdk/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java index 2d6e2474b6..9ca488e407 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java @@ -197,6 +197,12 @@ public final class ResumeSessionRequest { @JsonProperty("skillDirectories") private List skillDirectories; + @JsonProperty("agentDirectories") + private List agentDirectories; + + @JsonProperty("hookDirectories") + private List hookDirectories; + @JsonProperty("instructionDirectories") private List instructionDirectories; @@ -209,6 +215,9 @@ public final class ResumeSessionRequest { @JsonProperty("toolSearch") private ToolSearchConfig toolSearch; + @JsonProperty("imageGeneration") + private ImageGenerationConfig imageGeneration; + @JsonProperty("memory") private MemoryConfiguration memory; @@ -993,6 +1002,31 @@ public void setSkillDirectories(List skillDirectories) { this.skillDirectories = skillDirectories; } + /** Gets explicit agent discovery roots. @return the directories */ + public List getAgentDirectories() { + return agentDirectories == null ? null : Collections.unmodifiableList(agentDirectories); + } + + /** + * Sets explicit agent discovery roots. @param agentDirectories the directories + */ + public void setAgentDirectories(List agentDirectories) { + this.agentDirectories = agentDirectories; + } + + /** Gets explicit file-hook discovery roots. @return the directories */ + public List getHookDirectories() { + return hookDirectories == null ? null : Collections.unmodifiableList(hookDirectories); + } + + /** + * Sets explicit file-hook discovery roots. @param hookDirectories the + * directories + */ + public void setHookDirectories(List hookDirectories) { + this.hookDirectories = hookDirectories; + } + /** Gets instruction directories. @return the instruction directories */ public List getInstructionDirectories() { return instructionDirectories == null ? null : Collections.unmodifiableList(instructionDirectories); @@ -1030,6 +1064,19 @@ public ToolSearchConfig getToolSearch() { return toolSearch; } + /** Gets image generation config. @return the image generation config */ + public ImageGenerationConfig getImageGeneration() { + return imageGeneration; + } + + /** + * Sets image generation config. @param imageGeneration the image generation + * config + */ + public void setImageGeneration(ImageGenerationConfig imageGeneration) { + this.imageGeneration = imageGeneration; + } + /** Sets tool-search config. @param toolSearch the tool-search config */ public void setToolSearch(ToolSearchConfig toolSearch) { this.toolSearch = toolSearch; diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/SessionConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/SessionConfig.java index 926706872b..a6828def9d 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/SessionConfig.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/SessionConfig.java @@ -43,6 +43,7 @@ public class SessionConfig { private String sessionId; + private String name; private String clientName; private String model; private String reasoningEffort; @@ -84,11 +85,14 @@ public class SessionConfig { private String agent; private InfiniteSessionConfig infiniteSessions; private List skillDirectories; + private List agentDirectories; + private List hookDirectories; private List includedBuiltinSkills; private List instructionDirectories; private List pluginDirectories; private LargeToolOutputConfig largeOutput; private ToolSearchConfig toolSearch; + private ImageGenerationConfig imageGeneration; private MemoryConfiguration memory; private List disabledSkills; private List disabledMcpServers; @@ -146,6 +150,31 @@ public SessionConfig setSessionId(String sessionId) { return this; } + /** + * Gets the create-time session name. + * + * @return the session name, or {@code null} if the session is unnamed + */ + public String getName() { + return name; + } + + /** + * Names the session at creation, as {@code session.name.set} would, and is + * validated the same way: non-empty, no leading or trailing whitespace, at most + * 100 UTF-16 units, no control characters, and no double quotes. A name also + * makes the session persist from the start, so it can be listed and resumed + * without ever running a turn. Local sessions only: cloud creation rejects it. + * + * @param name + * the session name + * @return this config instance for method chaining + */ + public SessionConfig setName(String name) { + this.name = name; + return this; + } + /** * Gets the client name used to identify the application using the SDK. * @@ -1235,7 +1264,11 @@ public SessionConfig setInfiniteSessions(InfiniteSessionConfig infiniteSessions) } /** - * Gets the skill directories. + * Gets explicit discovery-only skill roots, not general filesystem grants. + * Available without configuration discovery, subject to skill/managed disables. + * Relative paths resolve once when supplied against the request's effective + * working directory; retained roots do not move when it changes. Null and + * omission are equivalent; an empty list clears only the SDK contribution. * * @return the list of skill directory paths */ @@ -1259,6 +1292,59 @@ public SessionConfig setSkillDirectories(List skillDirectories) { return this; } + /** + * Gets explicit discovery-only agent roots, separate from custom agent objects. + * No general filesystem grant. Available without configuration discovery, + * subject to source/managed disables. Relative paths resolve once when supplied + * against the request's effective working directory; retained roots do not + * move. Null and omission are equivalent; an empty list clears only the SDK + * contribution. + * + * @return the agent directory paths + */ + public List getAgentDirectories() { + return agentDirectories == null ? null : Collections.unmodifiableList(agentDirectories); + } + + /** + * Sets explicit discovery-only agent roots. See {@link #getAgentDirectories()}. + * + * @param agentDirectories + * the agent directory paths + * @return this config for method chaining + */ + public SessionConfig setAgentDirectories(List agentDirectories) { + this.agentDirectories = agentDirectories == null ? null : new ArrayList<>(agentDirectories); + return this; + } + + /** + * Gets explicit discovery-only file-hook roots, separate from callback hooks. + * No general filesystem grant. Available without configuration discovery or + * file-hook enablement (repository-origin hooks only), subject to hook/managed + * disables. Relative paths resolve once when supplied against the request's + * effective working directory; retained roots do not move. Null and omission + * are equivalent; an empty list clears only the SDK contribution. + * + * @return the file-hook directory paths + */ + public List getHookDirectories() { + return hookDirectories == null ? null : Collections.unmodifiableList(hookDirectories); + } + + /** + * Sets explicit discovery-only file-hook roots. See + * {@link #getHookDirectories()}. + * + * @param hookDirectories + * the file-hook directory paths + * @return this config for method chaining + */ + public SessionConfig setHookDirectories(List hookDirectories) { + this.hookDirectories = hookDirectories == null ? null : new ArrayList<>(hookDirectories); + return this; + } + /** * Gets the runtime-bundled skill allowlist. * @@ -1282,7 +1368,12 @@ public SessionConfig setIncludedBuiltinSkills(List includedBuiltinSkills } /** - * Gets the additional directories to search for custom instruction files. + * Gets explicit discovery-only instruction roots using existing SDK formats. No + * general filesystem grant. Available without configuration discovery, subject + * to instruction/managed disables. Relative paths resolve once when supplied + * against the request's effective working directory; retained roots do not + * move. Null and omission are equivalent; an empty list clears only the SDK + * contribution. * * @return the list of instruction directory paths */ @@ -1353,6 +1444,29 @@ public ToolSearchConfig getToolSearch() { return toolSearch; } + /** + * Gets the non-persisted image generation opt-in. + * + * @return the image generation config, or null when unset + */ + @CopilotExperimental + public ImageGenerationConfig getImageGeneration() { + return imageGeneration; + } + + /** + * Sets the image generation opt-in. Re-supply after runtime restart. + * + * @param imageGeneration + * the image generation config + * @return this config for method chaining + */ + @CopilotExperimental + public SessionConfig setImageGeneration(ImageGenerationConfig imageGeneration) { + this.imageGeneration = imageGeneration; + return this; + } + /** * Sets the tool-search override configuration. When {@code null}, the runtime * default tool-search behavior applies. @@ -1624,7 +1738,9 @@ public SessionConfig clearRefreshCustomInstructions() { } /** - * Gets whether file-based hooks are enabled. + * Gets whether repository-origin file hooks from {@code .github/hooks/} and + * trusted repository-configured roots are enabled. Does not control explicit + * {@link #getHookDirectories()} or callback hooks. * * @return an {@link java.util.Optional} containing {@code true} to enable file * hooks or {@code false} to disable them, or @@ -2399,6 +2515,8 @@ public SessionConfig clone() { copy.agent = this.agent; copy.infiniteSessions = this.infiniteSessions; copy.skillDirectories = this.skillDirectories != null ? new ArrayList<>(this.skillDirectories) : null; + copy.agentDirectories = this.agentDirectories != null ? new ArrayList<>(this.agentDirectories) : null; + copy.hookDirectories = this.hookDirectories != null ? new ArrayList<>(this.hookDirectories) : null; copy.includedBuiltinSkills = this.includedBuiltinSkills != null ? new ArrayList<>(this.includedBuiltinSkills) : null; @@ -2408,6 +2526,7 @@ public SessionConfig clone() { copy.pluginDirectories = this.pluginDirectories != null ? new ArrayList<>(this.pluginDirectories) : null; copy.largeOutput = this.largeOutput; copy.toolSearch = this.toolSearch; + copy.imageGeneration = this.imageGeneration; copy.memory = this.memory; copy.disabledSkills = this.disabledSkills != null ? new ArrayList<>(this.disabledSkills) : null; copy.disabledMcpServers = this.disabledMcpServers != null ? new ArrayList<>(this.disabledMcpServers) : null; diff --git a/java/sdk/src/test/java/com/github/copilot/AgentInfoTest.java b/java/sdk/src/test/java/com/github/copilot/AgentInfoTest.java index 40654292f8..f2735cae7d 100644 --- a/java/sdk/src/test/java/com/github/copilot/AgentInfoTest.java +++ b/java/sdk/src/test/java/com/github/copilot/AgentInfoTest.java @@ -6,6 +6,9 @@ import static org.junit.jupiter.api.Assertions.*; +import java.util.List; +import java.util.Map; + import org.junit.jupiter.api.Test; import com.github.copilot.rpc.AgentInfo; @@ -22,6 +25,7 @@ void defaultValuesAreNull() { assertNull(agent.getDisplayName()); assertNull(agent.getDescription()); assertNull(agent.getModel()); + assertNull(agent.getHooks()); } @Test @@ -52,6 +56,21 @@ void modelGetterSetter() { assertEquals("alpha/sonnet", agent.getModel()); } + @Test + void hooksGetterSetterAndJsonRoundTrip() throws Exception { + Map>> hooks = Map.of("preToolUse", + List.of(Map.of("type", "command", "bash", "echo scoped-hook", "timeoutSec", 10))); + var agent = new AgentInfo().setName("coder").setHooks(hooks); + assertEquals(hooks, agent.getHooks()); + + var mapper = JsonRpcClient.getObjectMapper(); + var json = mapper.valueToTree(agent); + assertEquals(mapper.valueToTree(hooks), json.get("hooks")); + var restored = mapper.treeToValue(json, AgentInfo.class); + assertEquals(hooks, restored.getHooks()); + assertNull(mapper.readValue("{\"name\":\"plain-agent\"}", AgentInfo.class).getHooks()); + } + @Test void fluentChainingReturnsThis() { var agent = new AgentInfo().setName("coder").setDisplayName("Code Assistant") @@ -70,5 +89,6 @@ void fluentChainingReturnsSameInstance() { assertSame(agent, agent.setDisplayName("Test")); assertSame(agent, agent.setDescription("A test agent")); assertSame(agent, agent.setModel("alpha/sonnet")); + assertSame(agent, agent.setHooks(Map.of())); } } diff --git a/java/sdk/src/test/java/com/github/copilot/AutoTierIT.java b/java/sdk/src/test/java/com/github/copilot/AutoTierIT.java index d93cbf995b..1ce94b4b19 100644 --- a/java/sdk/src/test/java/com/github/copilot/AutoTierIT.java +++ b/java/sdk/src/test/java/com/github/copilot/AutoTierIT.java @@ -5,6 +5,8 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -20,6 +22,7 @@ import com.github.copilot.generated.rpc.SessionEventLogReadParams; import com.github.copilot.rpc.AutoTier; import com.github.copilot.rpc.CapiSessionOptions; +import com.github.copilot.rpc.CopilotClientOptions; import com.github.copilot.rpc.MessageOptions; import com.github.copilot.rpc.PermissionHandler; import com.github.copilot.rpc.ResumeSessionConfig; @@ -200,12 +203,19 @@ void shouldRestoreAndOverrideFastAutoTierOnColdResume() throws Exception { void shouldCommitFastAutoTierAfterSuccessfulTurn() throws Exception { ctx.configureForTest("auto_tier", "should_commit_fast_auto_tier_after_successful_turn"); - try (CopilotClient client = ctx.createClient(); + // Keep this invocation's detach trace outside the home removed by context + // teardown. + Path logDirectory = Files.createTempDirectory(Files.createDirectories(Path.of("target", "failsafe-reports")), + "auto-tier-detach-").toAbsolutePath(); + try (CopilotClient client = ctx.createClient(new CopilotClientOptions().setLogLevel("debug") + .setCliArgs(new String[]{"--log-dir", logDirectory.toString()})); CopilotSession session = client .createSession(new SessionConfig().setModel(MODEL_ID) .setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setCapi(new CapiSessionOptions() .setAutoTier(AutoTier.EFFICIENCY).setEnableWebSocketResponses(false))) .get(30, TimeUnit.SECONDS)) { + System.err.println( + "AutoTierIT detach diagnostics: session=" + session.getSessionId() + " logs=" + logDirectory); var modelChangedFuture = new CompletableFuture(); try (var subscription = session.on(SessionModelChangeEvent.class, event -> { if (event.getData().autoTier() == com.github.copilot.generated.AutoTier.FAST) { diff --git a/java/sdk/src/test/java/com/github/copilot/CliServerManagerTest.java b/java/sdk/src/test/java/com/github/copilot/CliServerManagerTest.java index 462fbc17cd..9dea8d8c2a 100644 --- a/java/sdk/src/test/java/com/github/copilot/CliServerManagerTest.java +++ b/java/sdk/src/test/java/com/github/copilot/CliServerManagerTest.java @@ -7,13 +7,22 @@ import static org.junit.jupiter.api.Assertions.*; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.net.ServerSocket; import java.net.URI; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; import java.nio.file.Path; +import java.util.List; import java.util.Map; +import java.util.concurrent.CompletionException; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import com.github.copilot.ffi.NativeRuntimeLoader; import com.github.copilot.rpc.CopilotClientMode; @@ -190,9 +199,167 @@ void processInfoWithNullPort() { assertNull(info.port()); } - // ===== resolveCliCommand tests (via startCliServer) ===== - // resolveCliCommand is private, so we test indirectly through startCliServer - // with specific cliPath values. + // ===== resolveCliCommand tests ===== + + @ParameterizedTest + @ValueSource(strings = {"NoDefaultCurrentDirectoryInExePath", "nodefaultcurrentdirectoryinexepath"}) + @EnabledOnOs(OS.WINDOWS) + void bareWindowsCliPathHonorsCurrentDirectorySearchOptOut(String optOutKey) throws Exception { + Path bin = Files.createDirectory(tempDir.resolve("bin")); + Files.createFile(tempDir.resolve("copilot.EXE")); + Path executable = Files.createFile(bin.resolve("copilot.EXE")); + var options = new CopilotClientOptions().setCliPath("copilot").setCwd(tempDir.toString()).setUseStdio(true) + .setEnvironment(Map.of("Path", bin.toString(), "PATHEXT", ".EXE", optOutKey, "")); + + assertClientAttemptsExecutable(options, executable); + } + + @Test + @EnabledOnOs(OS.WINDOWS) + void bareWindowsCliPathSearchesCurrentDirectoryWithoutOptOut() throws Exception { + Path bin = Files.createDirectory(tempDir.resolve("bin")); + Path executable = Files.createFile(tempDir.resolve("copilot.EXE")); + Files.createFile(bin.resolve("copilot.EXE")); + var options = new CopilotClientOptions().setCliPath("copilot").setCwd(tempDir.toString()).setUseStdio(true) + .setEnvironment(Map.of("PATH", bin.toString(), "PATHEXT", ".EXE")); + + assertClientAttemptsExecutable(options, executable); + } + + @ParameterizedTest + @ValueSource(booleans = {false, true}) + @EnabledOnOs(OS.WINDOWS) + void explicitWindowsCurrentDirectorySearchIsPreservedWithOptOut(boolean relativeCliPath) throws Exception { + Path executable = Files.createFile(tempDir.resolve(".").resolve("copilot.EXE")); + var options = new CopilotClientOptions().setCliPath(relativeCliPath ? ".\\copilot.EXE" : "copilot") + .setCwd(tempDir.toString()).setUseStdio(true).setEnvironment(Map.of("PATH", relativeCliPath ? "" : ".", + "PATHEXT", ".EXE", "NoDefaultCurrentDirectoryInExePath", "")); + + assertClientAttemptsExecutable(options, executable); + } + + @ParameterizedTest + @ValueSource(strings = {"", "C:\\invalid|entry"}) + @EnabledOnOs(OS.WINDOWS) + void invalidWindowsCommandIsNotPassedToShellWhenCurrentDirectorySearchIsDisabled(String path) { + var options = new CopilotClientOptions().setCliPath("copilot|invalid").setCwd(tempDir.toString()) + .setUseStdio(true) + .setEnvironment(Map.of("PATH", path, "PATHEXT", ".EXE", "NoDefaultCurrentDirectoryInExePath", "")); + + try (var client = new CopilotClient(options)) { + Throwable error = assertThrows(CompletionException.class, () -> client.start().join()); + while (error.getCause() != null) { + error = error.getCause(); + } + var invalidPath = assertInstanceOf(InvalidPathException.class, error); + assertEquals("copilot|invalid.EXE", invalidPath.getInput()); + } + } + + private static void assertClientAttemptsExecutable(CopilotClientOptions options, Path executable) { + // Empty executables expose the attempted OS launch path without running a + // child. + try (var client = new CopilotClient(options)) { + Throwable error = assertThrows(CompletionException.class, () -> client.start().join()); + while (!(error instanceof IOException) && error.getCause() != null) { + error = error.getCause(); + } + var launchError = assertInstanceOf(IOException.class, error); + assertTrue(launchError.getMessage().contains("\"" + executable + "\""), launchError::getMessage); + } + } + + @ParameterizedTest + @ValueSource(strings = {"tools/copilot.exe", "tools\\copilot", ".\\tools\\copilot.EXE"}) + @EnabledOnOs(OS.WINDOWS) + void relativeWindowsCliPathDoesNotSearchPathDirectories(String cliPath) throws Exception { + Path bin = Files.createDirectory(tempDir.resolve("bin")); + Path tools = Files.createDirectory(bin.resolve("tools")); + Path unrelated = Files.createFile(tools.resolve("copilot.EXE")); + var options = new CopilotClientOptions().setCliPath(cliPath).setCwd(tempDir.toString()).setUseStdio(false) + .setEnvironment(Map.of("PATH", bin.toString(), "PATHEXT", ".EXE")); + + try (var client = new CopilotClient(options)) { + Throwable error = assertThrows(CompletionException.class, () -> client.start().join()); + while (error.getCause() != null) { + error = error.getCause(); + } + var launchError = assertInstanceOf(IOException.class, error); + assertTrue(launchError.getMessage().startsWith("CLI process exited unexpectedly."), + launchError::getMessage); + assertFalse(launchError.getMessage().contains(unrelated.toString()), launchError::getMessage); + } + } + + @ParameterizedTest + @ValueSource(strings = {" C:\\invalid", "C:\\invalid ", "C:\\invalidentry", + "C:\\invalid|entry", "C:\\invalid?entry", "C:\\invalid*entry", "C:\\invalid\"entry"}) + @EnabledOnOs(OS.WINDOWS) + void windowsNativeExecutableSkipsMalformedPathEntries(String malformedEntry) throws Exception { + Path bin = Files.createDirectory(tempDir.resolve("bin with spaces")); + Path executable = Files.createFile(bin.resolve("copilot.EXE")); + var options = new CopilotClientOptions().setCliPath("copilot").setCwd(tempDir.toString()) + .setEnvironment(Map.of("Path", malformedEntry + ";\"" + bin + "\"", "PATHEXT", ".CMD;.EXE")); + + assertEquals(List.of(executable.toString(), "--server"), resolveCliCommand(new CliServerManager(options))); + } + + @ParameterizedTest + @ValueSource(strings = {".CMD", ".BAT"}) + @EnabledOnOs(OS.WINDOWS) + void windowsCommandWrapperAfterMalformedPathEntryUsesShell(String extension) throws Exception { + Path bin = Files.createDirectory(tempDir.resolve("bin")); + Files.createFile(bin.resolve("copilot" + extension)); + Files.createFile(bin.resolve("copilot.EXE")); + var options = new CopilotClientOptions().setCwd(tempDir.toString()) + .setEnvironment(Map.of(NativeRuntimeLoader.COPILOT_CLI_PATH_ENV, "copilot", "PATH", + "C:\\invalid|entry;" + bin, "PATHEXT", extension + ";.EXE")); + + assertEquals(List.of("cmd", "/c", "copilot", "--server"), resolveCliCommand(new CliServerManager(options))); + } + + @Test + @EnabledOnOs(OS.WINDOWS) + void windowsCommandFallsBackToShellWhenPathEntriesAreMalformed() throws Exception { + var options = new CopilotClientOptions().setCliPath("copilot").setCwd(tempDir.toString()) + .setEnvironment(Map.of("PATH", " C:\\invalid;C:\\invalid|entry")); + + assertEquals(List.of("cmd", "/c", "copilot", "--server"), resolveCliCommand(new CliServerManager(options))); + } + + @ParameterizedTest + @ValueSource(booleans = {false, true}) + @EnabledOnOs(OS.WINDOWS) + void explicitWindowsExecutablePathDoesNotDependOnPath(boolean absolute) throws Exception { + Path bin = Files.createDirectory(tempDir.resolve("bin")); + Path executable = Files.createFile(bin.resolve("copilot.exe")); + String cliPath = (absolute ? executable : tempDir.relativize(executable)).toString(); + var options = new CopilotClientOptions().setCliPath(cliPath).setCwd(tempDir.toString()) + .setEnvironment(Map.of("PATH", "C:\\invalid|entry")); + + assertEquals(List.of(executable.toString(), "--server"), resolveCliCommand(new CliServerManager(options))); + } + + @Test + @EnabledOnOs(OS.WINDOWS) + void malformedWindowsCliPathIsNotSkipped() { + var options = new CopilotClientOptions().setCliPath("copilot|invalid").setCwd(tempDir.toString()) + .setEnvironment(Map.of("PATH", "C:\\invalid|entry", "PATHEXT", ".EXE")); + + var error = assertThrows(InvocationTargetException.class, + () -> resolveCliCommand(new CliServerManager(options))); + var cause = assertInstanceOf(InvalidPathException.class, error.getCause()); + assertEquals("copilot|invalid.EXE", cause.getInput()); + } + + @SuppressWarnings("unchecked") + private static List resolveCliCommand(CliServerManager manager) throws Exception { + var resolve = CliServerManager.class.getDeclaredMethod("resolveCliCommand", String.class, List.class); + resolve.setAccessible(true); + return (List) resolve.invoke(manager, manager.resolveCliLaunch().executable(), List.of("--server")); + } + + // ===== startCliServer tests ===== // On Windows, "/nonexistent/copilot" is not an absolute path (no drive letter), // so resolveCliCommand wraps it with "cmd /c" and ProcessBuilder.start() diff --git a/java/sdk/src/test/java/com/github/copilot/ClientOptionsE2ETest.java b/java/sdk/src/test/java/com/github/copilot/ClientOptionsE2ETest.java index 04afa58596..169c39fdcb 100644 --- a/java/sdk/src/test/java/com/github/copilot/ClientOptionsE2ETest.java +++ b/java/sdk/src/test/java/com/github/copilot/ClientOptionsE2ETest.java @@ -80,8 +80,8 @@ void testShouldForwardAdvancedSessionCreationOptionsToTheCli() throws Exception try (var client = fake.createClient()) { var session = client.createSession(new SessionConfig().setSessionId("java-create-session") - .setClientName("java-e2e-client").setModel("gpt-5-mini").setReasoningEffort("low") - .setReasoningSummary("none").setContextTier("long_context") + .setName("java named at create").setClientName("java-e2e-client").setModel("gpt-5-mini") + .setReasoningEffort("low").setReasoningSummary("none").setContextTier("long_context") .setAvailableTools(java.util.List.of("bash")).setExcludedTools(java.util.List.of("grep")) .setExcludedBuiltInAgents(java.util.List.of("explore")).setEnableSessionTelemetry(true) .setEnableCitations(true).setSessionLimits(new SessionLimitsConfig(42.0)) @@ -100,6 +100,7 @@ void testShouldForwardAdvancedSessionCreationOptionsToTheCli() throws Exception var create = fake.capturedRequest("session.create").path("params"); assertEquals("java-create-session", create.path("sessionId").asText()); + assertEquals("java named at create", create.path("name").asText()); assertEquals("java-e2e-client", create.path("clientName").asText()); assertEquals("gpt-5-mini", create.path("model").asText()); assertEquals("low", create.path("reasoningEffort").asText()); diff --git a/java/sdk/src/test/java/com/github/copilot/E2ETestContext.java b/java/sdk/src/test/java/com/github/copilot/E2ETestContext.java index 8a0e335758..fa96957d26 100644 --- a/java/sdk/src/test/java/com/github/copilot/E2ETestContext.java +++ b/java/sdk/src/test/java/com/github/copilot/E2ETestContext.java @@ -121,7 +121,7 @@ public static E2ETestContext create() throws IOException, InterruptedException { defaultUser.put("login", "e2e-test-user"); defaultUser.put("id", 12345); defaultUser.put("copilot_plan", "individual_pro"); - defaultUser.put("endpoints", Map.of("api", proxyUrl, "telemetry", "https://localhost:1/telemetry")); + defaultUser.put("endpoints", Map.of("api", proxyUrl, "telemetry", proxyUrl)); defaultUser.put("analytics_tracking_id", "e2e-test-tracking-id"); proxy.setCopilotUserByToken(DEFAULT_GITHUB_TOKEN, defaultUser); diff --git a/java/sdk/src/test/java/com/github/copilot/E2ETestContextTest.java b/java/sdk/src/test/java/com/github/copilot/E2ETestContextTest.java index bf307f6547..6e99145ac4 100644 --- a/java/sdk/src/test/java/com/github/copilot/E2ETestContextTest.java +++ b/java/sdk/src/test/java/com/github/copilot/E2ETestContextTest.java @@ -6,12 +6,37 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; import java.util.List; import org.junit.jupiter.api.Test; +import com.fasterxml.jackson.databind.ObjectMapper; + class E2ETestContextTest { + @Test + void defaultTelemetryEndpointUsesTheOwnedProxy() throws Exception { + try (E2ETestContext ctx = E2ETestContext.create()) { + var client = HttpClient.newHttpClient(); + var userRequest = HttpRequest.newBuilder(URI.create(ctx.getProxyUrl() + "/copilot_internal/user")) + .header("Authorization", "Bearer " + ctx.getEnvironment().get("GH_TOKEN")).build(); + var userResponse = client.send(userRequest, HttpResponse.BodyHandlers.ofString()); + assertEquals(200, userResponse.statusCode()); + var telemetryEndpoint = new ObjectMapper().readTree(userResponse.body()).path("endpoints").path("telemetry") + .asText(); + assertEquals(ctx.getProxyUrl(), telemetryEndpoint); + var telemetryRequest = HttpRequest.newBuilder(URI.create(telemetryEndpoint + "/telemetry")) + .POST(HttpRequest.BodyPublishers.ofString("{}\n")).build(); + var telemetryResponse = client.send(telemetryRequest, HttpResponse.BodyHandlers.ofString()); + assertEquals(204, telemetryResponse.statusCode()); + assertEquals("", telemetryResponse.body()); + } + } + @Test void expectedUserPromptsParseFoldedBlockScalars() { String snapshot = """ diff --git a/java/sdk/src/test/java/com/github/copilot/ImageGenerationConfigTest.java b/java/sdk/src/test/java/com/github/copilot/ImageGenerationConfigTest.java new file mode 100644 index 0000000000..5acace1e7a --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/ImageGenerationConfigTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +package com.github.copilot; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import com.fasterxml.jackson.databind.JsonNode; +import com.github.copilot.rpc.ImageGenerationConfig; +import com.github.copilot.rpc.ResumeSessionConfig; +import com.github.copilot.rpc.SessionConfig; + +class ImageGenerationConfigTest { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void createAndResumeForwardExplicitOptIn(boolean enabled) { + var imageGeneration = new ImageGenerationConfig().setEnabled(enabled); + var createConfig = new SessionConfig().setImageGeneration(imageGeneration).clone(); + var resumeConfig = new ResumeSessionConfig().setImageGeneration(imageGeneration).clone(); + assertSame(imageGeneration, createConfig.getImageGeneration()); + assertSame(imageGeneration, resumeConfig.getImageGeneration()); + var create = SessionRequestBuilder.buildCreateRequest(createConfig, "image-test"); + var resume = SessionRequestBuilder.buildResumeRequest("image-test", resumeConfig); + for (Object request : new Object[]{create, resume}) { + JsonNode json = JsonRpcClient.getObjectMapper().valueToTree(request); + assertEquals(enabled, json.get("imageGeneration").get("enabled").booleanValue()); + assertEquals(1, json.get("imageGeneration").size()); + } + } + + @Test + void createAndResumeOmitUnsetOptIn() { + var create = SessionRequestBuilder.buildCreateRequest(new SessionConfig(), "image-test"); + var resume = SessionRequestBuilder.buildResumeRequest("image-test", new ResumeSessionConfig()); + for (Object request : new Object[]{create, resume}) { + JsonNode json = JsonRpcClient.getObjectMapper().valueToTree(request); + assertFalse(json.has("imageGeneration")); + } + JsonNode empty = JsonRpcClient.getObjectMapper().valueToTree(new ImageGenerationConfig()); + assertEquals(0, empty.size()); + } +} diff --git a/java/sdk/src/test/java/com/github/copilot/JsonRpcClientTest.java b/java/sdk/src/test/java/com/github/copilot/JsonRpcClientTest.java index 8db208b53f..f296aba2e5 100644 --- a/java/sdk/src/test/java/com/github/copilot/JsonRpcClientTest.java +++ b/java/sdk/src/test/java/com/github/copilot/JsonRpcClientTest.java @@ -24,6 +24,8 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.copilot.generated.rpc.ServerRpc; +import com.github.copilot.generated.rpc.SandboxProxyCaCheckRelevanceParams; class JsonRpcClientTest { @@ -104,6 +106,26 @@ private String readRpcMessage(InputStream in) throws IOException { // ---- notify() ---- + @Test + void testGeneratedProxyCaQueriesPreserveBooleanResponses() throws Exception { + try (var pair = createSocketPair()) { + var server = new ServerRpc(pair.client::invoke); + for (boolean expected : new boolean[]{true, false}) { + for (String method : new String[]{"sandbox.proxyCa.checkInstallationSupport", + "sandbox.proxyCa.checkRelevance"}) { + CompletableFuture result = method.endsWith("checkInstallationSupport") + ? server.sandbox.proxyCa.checkInstallationSupport() + : server.sandbox.proxyCa.checkRelevance(new SandboxProxyCaCheckRelevanceParams(null)); + JsonNode request = MAPPER.readTree(readRpcMessage(pair.serverSide.getInputStream())); + assertEquals(method, request.get("method").asText()); + writeRpcMessage(pair.serverSide.getOutputStream(), + "{\"jsonrpc\":\"2.0\",\"id\":" + request.get("id") + ",\"result\":" + expected + "}"); + assertEquals(expected, result.get(5, TimeUnit.SECONDS)); + } + } + } + } + @Test void testNotify() throws Exception { try (var pair = createSocketPair()) { diff --git a/java/sdk/src/test/java/com/github/copilot/RpcHandlerDispatcherTest.java b/java/sdk/src/test/java/com/github/copilot/RpcHandlerDispatcherTest.java index a92a4b561d..a610007f0e 100644 --- a/java/sdk/src/test/java/com/github/copilot/RpcHandlerDispatcherTest.java +++ b/java/sdk/src/test/java/com/github/copilot/RpcHandlerDispatcherTest.java @@ -580,6 +580,10 @@ void skillProviderReadNullReturnsNullMarkdown() throws Exception { @Test void skillProviderFailuresUseStableMessagesWithoutData() throws Exception { + // Keep dispatch and logging outside the socket-read timeout. + var immediateDispatcher = new RpcHandlerDispatcher(sessions, lifecycleEvents::add, Runnable::run, + gitHubTokenProviders); + immediateDispatcher.registerHandlers(rpc); CopilotSession session = createSession("s1"); session.registerSkillProvider(skillProvider( () -> CompletableFuture.failedFuture(new IllegalStateException("secret list failure")), name -> { diff --git a/java/sdk/src/test/java/com/github/copilot/RpcWrappersTest.java b/java/sdk/src/test/java/com/github/copilot/RpcWrappersTest.java index 3fc2b499d1..c5309a20c0 100644 --- a/java/sdk/src/test/java/com/github/copilot/RpcWrappersTest.java +++ b/java/sdk/src/test/java/com/github/copilot/RpcWrappersTest.java @@ -50,9 +50,12 @@ import com.github.copilot.generated.rpc.SessionConnectorsRefreshParams; import com.github.copilot.generated.rpc.SessionConnectorsRefreshResult; import com.github.copilot.generated.rpc.SessionMcpListConfiguredResult; +import com.github.copilot.generated.rpc.SessionModelGetReasoningSummariesParams; +import com.github.copilot.generated.rpc.SessionModelGetReasoningSummariesResult; import com.github.copilot.generated.rpc.SessionModelSwitchToParams; import com.github.copilot.generated.rpc.SessionModelSwitchToRequest; import com.github.copilot.generated.rpc.SessionRpc; +import com.github.copilot.rpc.AgentInfo; /** * Unit tests for the generated RPC wrapper classes ({@link ServerRpc} and @@ -190,6 +193,33 @@ void serverRpc_mcp_discover_passes_params() { // ── SessionRpc tests ────────────────────────────────────────────────────── + @ParameterizedTest + @CsvSource({"true,false", "false,true", "null,null"}) + void sessionRpc_reasoning_summaries_accepts_only_host_state(String persisted, String visible) { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "reasoning-session"); + Boolean persistedShowReasoning = "null".equals(persisted) ? null : Boolean.valueOf(persisted); + Boolean summariesVisible = "null".equals(visible) ? null : Boolean.valueOf(visible); + var params = new SessionModelGetReasoningSummariesParams(persistedShowReasoning, summariesVisible); + + assertNull(params.sessionId()); + session.model.getReasoningSummaries(params).join(); + + assertEquals(1, stub.calls.size()); + var call = stub.calls.get(0); + assertEquals("session.model.getReasoningSummaries", call.method()); + assertEquals(SessionModelGetReasoningSummariesResult.class, call.resultType()); + var payload = assertInstanceOf(JsonNode.class, call.params()); + var expected = new ObjectMapper().createObjectNode().put("sessionId", "reasoning-session"); + if (persistedShowReasoning != null) { + expected.put("persistedShowReasoning", persistedShowReasoning); + } + if (summariesVisible != null) { + expected.put("summariesVisible", summariesVisible); + } + assertEquals(expected, payload); + } + @Test @AllowCopilotExperimental void sessionRpc_mcp_listConfigured_preserves_runtime_lifecycle() throws Exception { @@ -575,6 +605,56 @@ void serverRpc_account_getQuota_invokes_correct_method() { // RpcHandlerDispatcherTest) to construct a real CopilotSession and verify // that getRpc() returns a correctly wired SessionRpc. + @ParameterizedTest + @ValueSource(strings = {"list", "getCurrent", "select"}) + void copilotSession_agent_methods_deserialize_scoped_hooks(String method) throws Exception { + var mapper = JsonRpcClient.getObjectMapper(); + var agentJson = mapper.readTree(""" + { + "name": "hook-agent", + "displayName": "Hook Agent", + "description": "Agent with scoped hooks", + "hooks": { + "preToolUse": [{"type":"command","bash":"echo scoped-hook","timeoutSec":10}], + "sessionStart": [{"type":"command","bash":"echo started"}] + } + } + """); + try (var sockets = new SocketPair()) { + var stub = sockets.stubServer(); + var session = new CopilotSession("sess-agent-hooks", sockets.client()); + try { + CompletableFuture> future = switch (method) { + case "list" -> session.listAgents(); + case "getCurrent" -> session.getCurrentAgent().thenApply(List::of); + case "select" -> session.selectAgent("hook-agent").thenApply(List::of); + default -> throw new IllegalArgumentException(method); + }; + var request = stub.readOneMessage(); + assertEquals("session.agent." + method, request.get("method").asText()); + assertEquals("sess-agent-hooks", request.get("params").get("sessionId").asText()); + if ("select".equals(method)) { + assertEquals("hook-agent", request.get("params").get("name").asText()); + } + var result = mapper.createObjectNode(); + if ("list".equals(method)) { + result.putArray("agents").add(agentJson); + } else { + result.set("agent", agentJson); + } + stub.respond(request, result); + + var agents = future.get(3, TimeUnit.SECONDS); + assertEquals(1, agents.size()); + assertEquals("hook-agent", agents.get(0).getName()); + assertNotNull(agents.get(0).getHooks()); + assertEquals(agentJson.get("hooks"), mapper.valueToTree(agents.get(0).getHooks())); + } finally { + session.forceStopLocally(); + } + } + } + @Test void copilotSession_getRpc_returns_non_null_session_rpc() throws Exception { try (var sockets = new SocketPair()) { diff --git a/java/sdk/src/test/java/com/github/copilot/SessionEventHandlingTest.java b/java/sdk/src/test/java/com/github/copilot/SessionEventHandlingTest.java index 8f2edf52e0..64c32b4a74 100644 --- a/java/sdk/src/test/java/com/github/copilot/SessionEventHandlingTest.java +++ b/java/sdk/src/test/java/com/github/copilot/SessionEventHandlingTest.java @@ -295,8 +295,8 @@ void testSendAndWaitIgnoresChildEvents() throws Exception { dispatchEvent(childMessage); var childError = new SessionErrorEvent(); childError.setAgentId("child-1"); - childError.setData(new SessionErrorEvent.SessionErrorEventData("query", null, null, "child failed", null, - null, null, null, null, null)); + childError.setData(new SessionErrorEvent.SessionErrorEventData("query", null, null, null, null, + "child failed", null, null, null, null, null, null)); dispatchEvent(childError); var childIdle = createSessionIdleEvent(); childIdle.setAgentId("child-1"); diff --git a/java/sdk/src/test/java/com/github/copilot/SessionRequestBuilderTest.java b/java/sdk/src/test/java/com/github/copilot/SessionRequestBuilderTest.java index 978346e486..09b036c6fc 100644 --- a/java/sdk/src/test/java/com/github/copilot/SessionRequestBuilderTest.java +++ b/java/sdk/src/test/java/com/github/copilot/SessionRequestBuilderTest.java @@ -6,6 +6,8 @@ import static org.junit.jupiter.api.Assertions.*; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -100,6 +102,71 @@ void clearingTranscriptRecoveryRestoresRuntimeDefaultInAllModes() throws Excepti } } + @Test + void customizationDirectoriesArePreservedOnCreateAndResume() throws Exception { + var mapper = JsonRpcClient.getObjectMapper(); + for (int kind = 0; kind < 3; kind++) { + List roots = kind == 0 ? null : kind == 1 ? List.of() : List.of("relative-root", "other-root"); + var directories = new HashMap>(); + for (var type : List.of("skill", "agent", "instruction", "hook")) { + directories.put(type + "Directories", + roots == null ? null : roots.stream().map(root -> root + "-" + type).toList()); + } + var create = SessionRequestBuilder.buildCreateRequest( + new SessionConfig().setWorkingDirectory("request-cwd").setEnableConfigDiscovery(false) + .setEnableFileHooks(false).setSkillDirectories(directories.get("skillDirectories")) + .setAgentDirectories(directories.get("agentDirectories")) + .setInstructionDirectories(directories.get("instructionDirectories")) + .setHookDirectories(directories.get("hookDirectories")), + "created"); + var resume = SessionRequestBuilder.buildResumeRequest("resumed", + new ResumeSessionConfig().setWorkingDirectory("resume-cwd").setEnableConfigDiscovery(false) + .setEnableFileHooks(false).setSkillDirectories(directories.get("skillDirectories")) + .setAgentDirectories(directories.get("agentDirectories")) + .setInstructionDirectories(directories.get("instructionDirectories")) + .setHookDirectories(directories.get("hookDirectories"))); + for (var request : List.of(create, resume)) { + var wire = mapper.readTree(mapper.writeValueAsBytes(request)); + for (var entry : directories.entrySet()) { + if (entry.getValue() == null) { + assertFalse(wire.has(entry.getKey()), wire.toString()); + } else { + assertEquals(mapper.valueToTree(entry.getValue()), wire.get(entry.getKey())); + } + } + assertFalse(wire.path("enableConfigDiscovery").asBoolean(true)); + assertFalse(wire.path("enableFileHooks").asBoolean(true)); + assertFalse(wire.has("additionalDirectories")); + assertFalse(wire.has("customAgents")); + assertFalse(wire.path("hooks").asBoolean()); + } + } + } + + @Test + void customizationDirectoryClonesOwnTheirLists() { + var agents = new ArrayList<>(List.of("agents")); + var hooks = new ArrayList<>(List.of("hooks")); + var create = new SessionConfig().setAgentDirectories(agents).setHookDirectories(hooks); + var resume = new ResumeSessionConfig().setAgentDirectories(agents).setHookDirectories(hooks); + var createClone = create.clone(); + var resumeClone = resume.clone(); + agents.add("added-agents"); + hooks.add("added-hooks"); + assertEquals(List.of("agents"), create.getAgentDirectories()); + assertEquals(List.of("hooks"), create.getHookDirectories()); + assertEquals(List.of("agents"), resume.getAgentDirectories()); + assertEquals(List.of("hooks"), resume.getHookDirectories()); + assertEquals(List.of("agents"), createClone.getAgentDirectories()); + assertEquals(List.of("hooks"), createClone.getHookDirectories()); + assertEquals(List.of("agents"), resumeClone.getAgentDirectories()); + assertEquals(List.of("hooks"), resumeClone.getHookDirectories()); + assertThrows(UnsupportedOperationException.class, () -> createClone.getAgentDirectories().add("mutation")); + assertThrows(UnsupportedOperationException.class, () -> resumeClone.getHookDirectories().add("mutation")); + assertThrows(UnsupportedOperationException.class, () -> create.getAgentDirectories().add("mutation")); + assertThrows(UnsupportedOperationException.class, () -> resume.getHookDirectories().add("mutation")); + } + // ========================================================================= // buildCreateRequest // ========================================================================= diff --git a/java/sdk/src/test/java/com/github/copilot/ffi/FfiRuntimeHostTest.java b/java/sdk/src/test/java/com/github/copilot/ffi/FfiRuntimeHostTest.java index 19d9694ce2..3891a9d483 100644 --- a/java/sdk/src/test/java/com/github/copilot/ffi/FfiRuntimeHostTest.java +++ b/java/sdk/src/test/java/com/github/copilot/ffi/FfiRuntimeHostTest.java @@ -77,7 +77,7 @@ public boolean connectionClose(int connectionId) { .setRemote(true).setMode(CopilotClientMode.EMPTY).setCliArgs(new String[]{"--extra-flag"}); FfiRuntimeHost host = new FfiRuntimeHost(binding, "/tmp/runtime.node"); - host.start("/tmp/entrypoint.js", options); + host.start("/tmp/entrypoint.js", "github/github-app", options); List argv = MAPPER.readValue(binding.argv, new TypeReference>() { }); @@ -92,6 +92,7 @@ public boolean connectionClose(int connectionId) { assertTrue(argv.contains("42")); assertTrue(argv.contains("--remote")); assertTrue(argv.contains("--extra-flag")); + assertTrue(argv.contains("--cli-login-sync-integration-id=github/github-app")); Map env = MAPPER.readValue(binding.env, new TypeReference>() { }); @@ -133,7 +134,7 @@ public boolean connectionClose(int connectionId) { }; try (FfiRuntimeHost host = new FfiRuntimeHost(binding, "/tmp/runtime.node")) { - host.start(null, new CopilotClientOptions().setLogLevel("debug")); + host.start(null, null, new CopilotClientOptions().setLogLevel("debug")); } List argv = MAPPER.readValue(argvJson.get(), new TypeReference>() { @@ -184,7 +185,7 @@ void enqueue(byte[] bytes) { }; FfiRuntimeHost host = new FfiRuntimeHost(binding, "test-lib", throwingStream); - assertDoesNotThrow(() -> host.start("/tmp/entrypoint", new CopilotClientOptions())); + assertDoesNotThrow(() -> host.start("/tmp/entrypoint", null, new CopilotClientOptions())); assertTrue(callbackReturned.get(), "callback should return normally even when enqueue throws"); } @@ -219,7 +220,7 @@ public boolean connectionClose(int connectionId) { }; FfiRuntimeHost host = new FfiRuntimeHost(binding, "test-lib"); - host.start("/tmp/entrypoint", new CopilotClientOptions()); + host.start("/tmp/entrypoint", null, new CopilotClientOptions()); assertDoesNotThrow(host::close); } @@ -310,7 +311,7 @@ public boolean connectionClose(int connectionId) { }; FfiRuntimeHost host = new FfiRuntimeHost(binding, "test-lib"); - host.start("/tmp/entrypoint", new CopilotClientOptions()); + host.start("/tmp/entrypoint", null, new CopilotClientOptions()); CompletableFuture writer = CompletableFuture.runAsync(() -> { try { @@ -389,7 +390,7 @@ void enqueue(byte[] bytes) { }; FfiRuntimeHost host = new FfiRuntimeHost(binding, "test-lib", blockingStream); - host.start("/tmp/entrypoint", new CopilotClientOptions()); + host.start("/tmp/entrypoint", null, new CopilotClientOptions()); assertNotNull(callbackRef.get()); ExecutorService executor = Executors.newFixedThreadPool(2); diff --git a/java/sdk/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcRecordsCoverageTest.java b/java/sdk/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcRecordsCoverageTest.java index 348a2d9f1a..ed47710539 100644 --- a/java/sdk/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcRecordsCoverageTest.java +++ b/java/sdk/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcRecordsCoverageTest.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.github.copilot.AllowCopilotExperimental; import com.github.copilot.TestUtil; +import com.github.copilot.generated.SessionManagedSettingsResolvedEvent.SessionManagedSettingsResolvedEventData; /** * Tests for generated RPC param and result record types. Exercises @@ -29,6 +30,27 @@ */ class GeneratedRpcRecordsCoverageTest { + @Test + @AllowCopilotExperimental + void managedPermissions_reuses_event_context_and_evaluated_operations() throws Exception { + var mapper = new ObjectMapper(); + var eventJson = mapper.readTree(""" + {"permissionsContext":{"permissions":{"_limitToLists":[["https://example.com"]]},"failClosed":false}} + """); + var eventData = mapper.treeToValue(eventJson, SessionManagedSettingsResolvedEventData.class); + var evaluation = mapper.readValue(""" + {"operation":{"kind":"url","url":"https://example.com/a"},"verdict":"allow"} + """, ManagedPermissionEvaluation.class); + var params = new ManagedSettingsPermissionsEvaluateParams(eventData.permissionsContext(), + List.of(evaluation.operation())); + var paramsJson = mapper.valueToTree(params); + assertEquals(eventJson.get("permissionsContext"), paramsJson.get("context")); + assertEquals("url", paramsJson.get("operations").get(0).get("kind").asText()); + assertEquals("https://example.com/a", paramsJson.get("operations").get(0).get("url").asText()); + assertEquals(ManagedPermissionOperation.ManagedPermissionOperationKind.URL, evaluation.operation().kind()); + assertEquals(params, mapper.treeToValue(paramsJson, ManagedSettingsPermissionsEvaluateParams.class)); + } + // ── Params records ───────────────────────────────────────────────────── @Test @@ -701,7 +723,7 @@ void pingResult_fields() { @Test void sessionAgentListResult_with_items() { var item = new AgentInfo("name1", "Name One", "Desc 1", "/path/to/agent1", null, null, null, null, null, null, - null, null, null, null, null, null); + null, null, null, null, null, null, null); var result = new SessionAgentListResult(List.of(item)); assertEquals(1, result.agents().size()); assertEquals("name1", result.agents().get(0).name()); @@ -713,7 +735,7 @@ void sessionAgentListResult_with_items() { @Test void sessionAgentGetCurrentResult_nested() { var agent = new AgentInfo("agent-1", "Agent One", "Does things", null, null, null, null, null, null, null, null, - null, null, null, null, null); + null, null, null, null, null, null); var result = new SessionAgentGetCurrentResult(agent); assertEquals("agent-1", result.agent().name()); assertEquals("Agent One", result.agent().displayName()); @@ -730,18 +752,25 @@ void sessionAgentGetCurrentResult_null_agent() { @Test void sessionAgentReloadResult_with_items() { var item = new AgentInfo("a", "A", "Desc", "/path/to/a", null, null, null, null, null, null, null, null, null, - null, null, null); + null, null, null, null); var result = new SessionAgentReloadResult(List.of(item)); assertEquals(1, result.agents().size()); assertEquals("a", result.agents().get(0).name()); } @Test - void sessionAgentSelectResult_nested() { + void sessionAgentSelectResult_nested() throws Exception { + Map> hooks = Map.of("Stop", + List.of(Map.of("type", "command", "command", "echo scoped-stop"))); var agent = new AgentInfo("selected", "Selected", "The selected agent", "/path/to/selected", null, null, null, - null, null, null, null, null, null, null, null, null); + null, null, null, null, null, null, null, null, null, hooks); var result = new SessionAgentSelectResult(agent); assertEquals("selected", result.agent().name()); + var mapper = new ObjectMapper(); + var json = mapper.writeValueAsString(result); + assertEquals("echo scoped-stop", + mapper.readTree(json).get("agent").get("hooks").get("Stop").get(0).get("command").asText()); + assertEquals(hooks, mapper.readValue(json, SessionAgentSelectResult.class).agent().hooks()); } @Test diff --git a/nodejs/README.md b/nodejs/README.md index 1566ea9cad..2e86f7d603 100644 --- a/nodejs/README.md +++ b/nodejs/README.md @@ -671,6 +671,23 @@ if (result.status === "pending") { See [Auto tier persistence](../docs/features/session-persistence.md#auto-tier-persistence) for the full lifecycle rules. +##### `rpc.model.getReasoningSummaries(params)` (experimental) + +Read the current model's reasoning-summary presentation policy without changing session state. Call `session.rpc.model.list()` first to load the model catalog; otherwise the result may have `ready: false`. The request and result types are exported from `@github/copilot-sdk`. + +```typescript +import type { + ModelGetReasoningSummariesRequest, + ModelGetReasoningSummariesResult, +} from "@github/copilot-sdk"; + +const presentation: ModelGetReasoningSummariesRequest = { summariesVisible: false }; +const policy: ModelGetReasoningSummariesResult = + await session.rpc.model.getReasoningSummaries(presentation); +``` + +Omit `persistedShowReasoning` when no visibility setting is saved. `visibility` can remain unset while experiment assignment is pending; `clearRedundantSetting` tells the host when its persisted setting matches the model default. + ##### `setTools(tools: Tool[]): Promise` Replace the tools this client supplies to the session, together with the handlers that serve them (experimental). `tools` takes the same definitions as `createSession` and becomes this client's complete tool set; pass `[]` to remove all of this client's tools. Built-in, MCP, and plugin tools, and tools other connected clients supply, are unaffected. @@ -1377,6 +1394,36 @@ const session = await client.createSession({ }); ``` +### Managed network policy for client-owned browsers + +Obtain a reusable `permissionsContext` from +`client.rpc.managedSettings.resolve({ selectionId })` for account and device +policy, or `session.rpc.managedSettings.get()` for the session's retained policy, +including SDK injection and policy-helper rules. Omit `selectionId` for the current +account, or device policy only when signed out. Resolution uses the managed-policy +cache and can fetch server policy; keep it off latency-critical startup paths. +Older runtimes may omit `permissionsContext`. Treat an absent context as unsupported +evaluation, not unmanaged policy; do not navigate without a policy decision. + +Call `client.rpc.managedSettings.permissions.evaluate({ context: permissionsContext, operations: [{ kind: "url", url: "https://docs.example.com/guide" }] })`. +This is pure, sessionless evaluation: it performs no policy discovery, I/O, +prompting, or sandbox changes. Reuse an in-memory context for repeated checks and +refresh it when managed policy changes. Retain the runtime-owned `permissions` +object verbatim rather than rebuilding it from the flattened settings display. + +The result is `{ results: [{ operation, verdict }], failClosed }`, with one entry per +input operation in the same order. `deny` blocks navigation, `ask` requires approval, +`allow` approves under managed policy, and `unmanaged` leaves the consumer's normal +permission flow in effect. A check never prompts or grants a saved approval. +Invalid URLs reject the batch. Unknown policy rejects or returns `failClosed: true` +with all URLs denied; do not navigate on an error. + +Check every navigation, redirect, and new-window destination before opening it. +These APIs evaluate managed permission rules only: they do not replace browser +security checks, user permissions, or sandbox policy. `permissions.limitTo` neither +enables the sandbox nor changes `sandbox.userPolicy.network`; configure sandbox +network restrictions explicitly. + ### Permission Result Kinds The handler must return one of the `PermissionDecision` shapes (or `{ kind: "no-result" }`). Approval scopes are present-tense — they describe the decision to apply, not the outcome reported back on session events: @@ -1605,6 +1652,26 @@ npm ci npm test ``` +### Internal feature-flag example + +[`examples/internal-feature-flags.ts`](examples/internal-feature-flags.ts) uses +the generated internal RPC factory against a real runtime over stdio. +It lists the catalog and shows environment, settings, and explicit-override +precedence. It also checks that static resolution does not apply `COPILOT_EXP_*` +experiment assignments. It creates no session and sends no model request. + +After you prepare matching runtime artifacts, run this from the runtime repository root: + +```bash +COPILOT_RUNTIME_PROVIDER_LIB="$PWD/bazel-bin/src/native/runtime/runtime.node" \ + src/sdk/nodejs/node_modules/.bin/tsx \ + src/sdk/nodejs/examples/internal-feature-flags.ts \ + "$PWD/bazel-bin/src/runtime-bin/copilot-runtime" +``` + +The JSON output includes `staticResolution` values of `false`, `true`, `false`, +and `true`, in that order. These methods are internal, not public SDK APIs. + ## License MIT diff --git a/nodejs/examples/internal-feature-flags.ts b/nodejs/examples/internal-feature-flags.ts new file mode 100644 index 0000000000..83824012fc --- /dev/null +++ b/nodejs/examples/internal-feature-flags.ts @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import assert from "node:assert/strict"; +import { spawn } from "node:child_process"; +import { once } from "node:events"; +import { mkdtemp, rm } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join, resolve } from "node:path"; +import { createMessageConnection } from "vscode-jsonrpc/node.js"; +import { createInternalServerRpc } from "../src/generated/rpc.js"; + +async function showFeatureFlags(rpc: ReturnType) { + await rpc.connect({}); + const { flags } = await rpc.featureFlags.listMetadata(); + assert.equal(flags.TASK_ANALYTICS.name, "TASK_ANALYTICS"); + assert.equal(flags.TASK_ANALYTICS.expFlagEnvVar, "COPILOT_EXP_COPILOT_CLI_TASK_ANALYTICS"); + + const population = { isStaff: false, isExperimental: false, isTeam: false }; + const experimentOnly = await rpc.featureFlags.resolve({ + ...population, + environment: { COPILOT_EXP_COPILOT_CLI_TASK_ANALYTICS: "true" }, + }); + assert.equal(experimentOnly.TASK_ANALYTICS, false); + + const request = { + ...population, + baseFlags: { TASK_ANALYTICS: false }, + environment: { TASK_ANALYTICS: "true" }, + }; + const environment = await rpc.featureFlags.resolve(request); + const config = { enabledFeatureFlags: { TASK_ANALYTICS: false } }; + const settings = await rpc.featureFlags.resolve({ ...request, config }); + const explicit = await rpc.featureFlags.resolve({ + ...request, + config, + flagOverrides: { TASK_ANALYTICS: true }, + }); + assert.deepEqual(environment, { TASK_ANALYTICS: true }); + assert.deepEqual(settings, { TASK_ANALYTICS: false }); + assert.deepEqual(explicit, { TASK_ANALYTICS: true }); + + console.log( + JSON.stringify({ + declaredFlags: Object.keys(flags).length, + metadata: flags.TASK_ANALYTICS, + staticResolution: { + experimentOnly: experimentOnly.TASK_ANALYTICS, + environment: environment.TASK_ANALYTICS, + settings: settings.TASK_ANALYTICS, + explicit: explicit.TASK_ANALYTICS, + }, + }) + ); +} + +const runtimePath = process.argv[2]; +if (!runtimePath) { + throw new Error("Usage: tsx examples/internal-feature-flags.ts "); +} + +const home = await mkdtemp(join(tmpdir(), "copilot-feature-flags-")); +const child = spawn( + resolve(runtimePath), + ["--server", "--stdio", "--no-auto-login", "--no-auto-update", "--no-remote"], + { + cwd: home, + env: { ...process.env, COPILOT_HOME: home, COPILOT_CONNECTION_TOKEN: "" }, + stdio: ["pipe", "pipe", "inherit"], + windowsHide: true, + } +); +const closed = once(child, "close"); +const connection = createMessageConnection(child.stdout, child.stdin); +connection.listen(); +try { + await Promise.race([ + showFeatureFlags(createInternalServerRpc(connection)), + closed.then(([code, signal]) => { + throw new Error( + `Runtime exited before the example completed: code=${code}, signal=${signal}` + ); + }), + ]); +} finally { + connection.dispose(); + child.stdin.end(); + try { + const [code, signal] = await closed; + assert.equal(code, 0, `Runtime exit signal: ${signal}`); + } finally { + await rm(home, { recursive: true, force: true }); + } +} diff --git a/nodejs/examples/internal-user-settings.ts b/nodejs/examples/internal-user-settings.ts new file mode 100644 index 0000000000..8bc4641903 --- /dev/null +++ b/nodejs/examples/internal-user-settings.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import type { createInternalServerRpc } from "../src/generated/rpc.js"; + +// Internal host example; these methods are not part of the public SDK API. +export async function editUserSettings( + settings: ReturnType["user"]["settings"], + configDir: string +) { + const validation = await settings.validate({ + topKey: "voice", + value: { enabled: true }, + }); + if (!validation.ok) { + throw new Error(JSON.stringify(validation.issues)); + } + await settings.update({ + key: "voice", + value: { enabled: true }, + configDir, + }); + return settings.update({ + key: "voice", + value: { selectedModel: null }, + configDir, + }); +} diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index e8104e0779..fc233f8b94 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -28,13 +28,12 @@ "json-schema": "^0.4.0", "json-schema-to-typescript": "^15.0.4", "prettier": "^3.8.1", - "quicktype-core": "^23.2.6", "rimraf": "^6.1.2", "semver": "^7.7.3", "tar": "^7.5.22", "tsx": "^4.20.6", "typescript": "^5.0.0", - "vitest": "^4.0.18", + "vitest": "^4.1.11", "ws": "^8.21.0" }, "engines": { @@ -58,37 +57,6 @@ "url": "https://github.com/sponsors/philsturgeon" } }, - "node_modules/@emnapi/core": { - "version": "1.10.0", - "integrity": "sha1-OAzMjyQS6iLR2XLff47iOjucdGc=", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "integrity": "sha1-SyYMDTU0IE6YxhELjbGph9JuyHw=", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "integrity": "sha1-KP7SGhuhznl8RKBwq8lNQvOuhUg=", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@esbuild/aix-ppc64": { "version": "0.28.1", "integrity": "sha1-egGo0uwvuy2seK2tCbD6eB5Agr4=", @@ -660,12 +628,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@glideapps/ts-necessities": { - "version": "2.2.3", - "integrity": "sha512-gXi0awOZLHk3TbW55GZLCPP6O+y/b5X1pBXKBVckFONSwF1z1E5ND2BGJsghQFah+pW7pkkyFb2VhUQI2qhL5w==", - "dev": true, - "license": "MIT" - }, "node_modules/@hpke/chacha20poly1305": { "version": "1.8.0", "integrity": "sha1-1NL248zgi0xVU5IOFbTlmMildy4=", @@ -724,27 +686,40 @@ } }, "node_modules/@humanfs/core": { - "version": "0.19.1", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "version": "0.19.2", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, "engines": { "node": ">=18.18.0" } }, "node_modules/@humanfs/node": { - "version": "0.16.7", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "version": "0.16.8", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.1", + "@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", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", @@ -784,8 +759,8 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "version": "1.6.0", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", "dev": true, "license": "MIT" }, @@ -1072,34 +1047,13 @@ "dev": true, "license": "MIT" }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.2.3", - "integrity": "sha1-l+PUXXQk3F2h1OMvO/OykvbBtEw=", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.3" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=23.5.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", - "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" - } - }, "node_modules/@oxc-project/types": { - "version": "0.133.0", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "version": "0.153.0", + "integrity": "sha512-jSNrRIyvPVLL5E1z/tS5zLAprosGz/FuI1PWwEOz59j3E4TTTzSpqKb/RDGqJhhW0QHYISGXxq582wGx6T11Tw==", "dev": true, "license": "MIT", "funding": { - "url": "https://github.com/sponsors/Boshen" + "url": "https://github.com/sponsors/oxc-project" } }, "node_modules/@platformatic/vfs": { @@ -1111,9 +1065,25 @@ "node": ">= 22" } }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.13", + "integrity": "sha512-Vu/NICrgdRvUOeaJZIyB2FP63s6fO/4ud5l8dXmxDx9yDyn+mN0y+gsvsVviSli+T361Aqo0h46EuMoobrwawA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.3", - "integrity": "sha1-VM6Pg4IhP0oxSgwve6g/gf/q5ZI=", + "version": "1.2.13", + "integrity": "sha512-6LEoIrGy5vvCd2boNnv2soPSUZj/PRvhp8R/t9KlX+mIZxThIE/ZqxrGXxmuNX0FuvQFvLdZr7S4HrYpYBVhEQ==", "cpu": [ "arm64" ], @@ -1128,8 +1098,8 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.3", - "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", + "version": "1.2.13", + "integrity": "sha512-CaJSRzlQ6Q+zkDMVA9OkVxBjVoPRCDEE3v0FSJ9kIQBgRjFApA3Si4ijhbNVUji7ZJt+PfO0e46qy4nPInHUPA==", "cpu": [ "arm64" ], @@ -1144,8 +1114,8 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.3", - "integrity": "sha1-U/V94fWZ7PHbE4I8/IjBj7gJVK0=", + "version": "1.2.13", + "integrity": "sha512-e4r340I5hxSzJkD3YJBxgzq/rJ3wwxwdLLb2FPt1Zpyem20nrQPT11DDEEkazwxaHumH8wpglsp/Ozgj/wT6mw==", "cpu": [ "x64" ], @@ -1160,8 +1130,8 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.3", - "integrity": "sha1-bz/dobeuqsnSaKUmgEtPuW5ONfE=", + "version": "1.2.13", + "integrity": "sha512-hoBWDZ4/K/mJKVMbsgUd1jfpA2DiOrJ54mpegObU9TBHBBrtX4FLIXJJQRDPZ/2Dn91GvPob+Z2hwKJzf4E5SQ==", "cpu": [ "x64" ], @@ -1176,8 +1146,8 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.3", - "integrity": "sha1-2HpFS/WFzJZ2hJN36R1uN1KXMm8=", + "version": "1.2.13", + "integrity": "sha512-oF9fZu3vTWnTiVAkDFxU9PJLt1nh20K9RRpuHsR7ZnE2L7XookBbDBVLojCVSLGbvtSlxeUNnjAj6DbNsKyzbg==", "cpu": [ "arm" ], @@ -1192,12 +1162,15 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.3", - "integrity": "sha1-QZ/Wv2Es80jxBSjLzZTrq5YH2NE=", + "version": "1.2.13", + "integrity": "sha512-DTpb/+jMiqWMPgphn4ksW1zW+XLJ6dJJ9FMvnIk9I291LhCctMPhMYJrP5t9p6C9qa3KXadDdUEZHBaPh3kNxQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1208,12 +1181,15 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.3", - "integrity": "sha1-/MaRhpa7doRId+HkkwoY/Q03QGk=", + "version": "1.2.13", + "integrity": "sha512-xU+Kvv9N2A3SY35zFh1FWWNjxbadE/BKeqnkXe7vScU4GZsw757BlVmDLITZEq8DPpk6m7uoDYkK7P+x/uU5Ow==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1224,12 +1200,15 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.3", - "integrity": "sha1-Mq7LfI2uXU8qjN5XoFjshpkVQvg=", + "version": "1.2.13", + "integrity": "sha512-XMk/hnhh25oQ756Lclb1Q11h4ll0xC0Esa5w4Vzp+mAE2S3AGJgeM0SonrIgY8fHhjarb6hcoTuwoFG0VVP2ig==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1240,12 +1219,15 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.3", - "integrity": "sha1-vtk0bqgea7i5PPEfXYi3fbiQt2M=", + "version": "1.2.13", + "integrity": "sha512-U1BoTHYmlywE2n3HUWjzZ+8EQsKr+OwpxYuDWaubx42tUL8QSL7nOqRz/NVHanibbFP3M1Rxuprz/wNcbMKjqw==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1256,12 +1238,15 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.3", - "integrity": "sha1-ZMLSb3Xf/ZtaH5dVegCudyUMjLc=", + "version": "1.2.13", + "integrity": "sha512-J4cm80Pc6wEwV5AZ42o62sIQgDk+Y7R2s646jIwuvO+4V46QB58N4vVuMPWCBaNgursXLpmUWJV//WQAkPvHBQ==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1272,12 +1257,15 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.3", - "integrity": "sha1-WkUTLopHZZ7qrztUDClUqXyGD/M=", + "version": "1.2.13", + "integrity": "sha512-W0euZS40U4V6JkmEnVCIX9mcsL6cbXQ4X8c2w+MMOIjFg68TXUW+u3RT+2e1t1pv9mg/EP3pDtK3POXdQ7Rxyw==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1288,8 +1276,8 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.3", - "integrity": "sha1-KQUTBoxV6EnchFejKv7h17Csswk=", + "version": "1.2.13", + "integrity": "sha512-do/AF/4n9MRshia3a5aXoy8xM2c6mgfT6D7H/dY6my5z+hfCIR4ab7mk4IRj++nsEQpKy39kd0DQnDq+r692tw==", "cpu": [ "arm64" ], @@ -1303,27 +1291,9 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.3", - "integrity": "sha1-PZly2/GpU9PHr6pKDyDvKy458xs=", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.3", - "integrity": "sha1-oASrYHoW1vA7y1VXKP+IivdXc60=", + "version": "1.2.13", + "integrity": "sha512-6FtUf2cJZobutJdxnGYX8z0vtbgqU0b2v8vfBhkXb9wp2MppMFtkunE+IaOntyi01rkIQoPW2GHABtfzsC0BRQ==", "cpu": [ "arm64" ], @@ -1338,8 +1308,8 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.3", - "integrity": "sha1-4qJbNGkaHMihIJ195wkGMCbdDNs=", + "version": "1.2.13", + "integrity": "sha512-UKhFaZTeTcbKrb3iU82m17jVcPEAoqDkCN5MBqgPyZSwp/cQmib21crTS0DdH7T1B7wZRWEI89YQAOJMet5yhg==", "cpu": [ "x64" ], @@ -1365,16 +1335,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.3", - "integrity": "sha1-AVy6np3UfOFNA9KoxdVHv7FpZl0=", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/chai": { "version": "5.2.3", "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", @@ -1656,15 +1616,15 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.8", - "integrity": "sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==", + "version": "4.1.11", + "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.8", - "@vitest/utils": "4.1.8", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -1673,12 +1633,12 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.8", - "integrity": "sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==", + "version": "4.1.11", + "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.8", + "@vitest/spy": "4.1.11", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -1699,8 +1659,8 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.8", - "integrity": "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==", + "version": "4.1.11", + "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==", "dev": true, "license": "MIT", "dependencies": { @@ -1711,12 +1671,12 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.8", - "integrity": "sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==", + "version": "4.1.11", + "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.8", + "@vitest/utils": "4.1.11", "pathe": "^2.0.3" }, "funding": { @@ -1724,13 +1684,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.8", - "integrity": "sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==", + "version": "4.1.11", + "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.8", - "@vitest/utils": "4.1.8", + "@vitest/pretty-format": "4.1.11", + "@vitest/utils": "4.1.11", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -1739,8 +1699,8 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.8", - "integrity": "sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==", + "version": "4.1.11", + "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", "dev": true, "license": "MIT", "funding": { @@ -1748,12 +1708,12 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.8", - "integrity": "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==", + "version": "4.1.11", + "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.8", + "@vitest/pretty-format": "4.1.11", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -1761,18 +1721,6 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/abort-controller": { - "version": "3.0.0", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dev": true, - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, "node_modules/acorn": { "version": "8.15.0", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", @@ -1831,29 +1779,9 @@ "dev": true, "license": "MIT" }, - "node_modules/base64-js": { - "version": "1.5.1", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/brace-expansion": { - "version": "1.1.16", - "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "version": "1.1.21", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -1861,36 +1789,6 @@ "concat-map": "0.0.1" } }, - "node_modules/browser-or-node": { - "version": "3.0.0", - "integrity": "sha512-iczIdVJzGEYhP5DqQxYM9Hh7Ztpqqi+CXZpSmX8ALFs9ecXkQIeqRyM6TfxEfMVpwhl3dSuDvxdzzo9sUOIVBQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/buffer": { - "version": "6.0.3", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/callsites": { "version": "3.1.0", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", @@ -1901,8 +1799,8 @@ } }, "node_modules/chai": { - "version": "6.2.2", - "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "version": "6.3.0", + "integrity": "sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==", "dev": true, "license": "MIT", "engines": { @@ -1949,12 +1847,6 @@ "node": ">=18" } }, - "node_modules/collection-utils": { - "version": "1.0.1", - "integrity": "sha512-LA2YTIlR7biSpXkKYwwuzGjwL5rjWEZVOSnvdUc7gObvWe4WkjxOpfrdhoP7Hs09YWDVfg0Mal9BpAqLfVEzQg==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/color-convert": { "version": "2.0.1", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", @@ -1985,15 +1877,6 @@ "dev": true, "license": "MIT" }, - "node_modules/cross-fetch": { - "version": "4.1.0", - "integrity": "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "node-fetch": "^2.7.0" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", @@ -2299,24 +2182,6 @@ "node": ">=0.10.0" } }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/events": { - "version": "3.3.0", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, "node_modules/expect-type": { "version": "1.3.0", "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", @@ -2488,26 +2353,6 @@ "node": ">=16.0.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/ignore": { "version": "7.0.5", "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", @@ -2563,24 +2408,12 @@ "node": ">=0.10.0" } }, - "node_modules/is-url": { - "version": "1.2.4", - "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", - "dev": true, - "license": "MIT" - }, "node_modules/isexe": { "version": "2.0.0", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true, "license": "ISC" }, - "node_modules/js-base64": { - "version": "3.7.8", - "integrity": "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/js-yaml": { "version": "4.3.2", "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", @@ -2702,8 +2535,8 @@ } }, "node_modules/lightningcss": { - "version": "1.32.0", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "version": "1.33.0", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -2717,22 +2550,22 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" } }, "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "integrity": "sha1-8DOIURbf79nG9UeHUj41FLYeGWg=", + "version": "1.33.0", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", "cpu": [ "arm64" ], @@ -2751,8 +2584,8 @@ } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "version": "1.33.0", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", "cpu": [ "arm64" ], @@ -2771,8 +2604,8 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "integrity": "sha1-NfPpczLRMLnKGB4RtWje1q68bV4=", + "version": "1.33.0", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", "cpu": [ "x64" ], @@ -2791,8 +2624,8 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "integrity": "sha1-l3enZHK2Ttb/lDQq1kx7r9eUpXU=", + "version": "1.33.0", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", "cpu": [ "x64" ], @@ -2811,8 +2644,8 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "integrity": "sha1-E65lLhq3O5E117faFy9mbEEK1T0=", + "version": "1.33.0", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", "cpu": [ "arm" ], @@ -2831,12 +2664,15 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "integrity": "sha1-QXhYeVqUWS9oASOhsfnaig4e8zU=", + "version": "1.33.0", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -2851,12 +2687,15 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "integrity": "sha1-a+NmkugQtxgECAL9gJYjz/5zITM=", + "version": "1.33.0", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -2871,12 +2710,15 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "integrity": "sha1-C3gDr06yHP043Tn+Kru1PH3QkfY=", + "version": "1.33.0", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -2891,12 +2733,15 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "integrity": "sha1-iNyLqGXd3bGsXvBLDxYYBEGMFjs=", + "version": "1.33.0", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -2911,8 +2756,8 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "integrity": "sha1-TzC6P6XpJfW3n5RejMDRdsOxqzg=", + "version": "1.33.0", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", "cpu": [ "arm64" ], @@ -2931,8 +2776,8 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "integrity": "sha1-FBqlYFZFBkkokCu0rwRfp9n0Igo=", + "version": "1.33.0", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", "cpu": [ "x64" ], @@ -3020,8 +2865,8 @@ } }, "node_modules/minimatch/node_modules/brace-expansion": { - "version": "5.0.8", - "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", + "version": "5.0.12", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3068,8 +2913,8 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.17", - "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==", + "version": "3.3.20", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", "dev": true, "funding": [ { @@ -3091,26 +2936,6 @@ "dev": true, "license": "MIT" }, - "node_modules/node-fetch": { - "version": "2.7.0", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "node_modules/obug": { "version": "2.1.1", "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", @@ -3174,12 +2999,6 @@ "dev": true, "license": "BlueOak-1.0.0" }, - "node_modules/pako": { - "version": "1.0.11", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true, - "license": "(MIT AND Zlib)" - }, "node_modules/parent-module": { "version": "1.0.1", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", @@ -3239,8 +3058,8 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.4", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.7", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "engines": { @@ -3250,18 +3069,9 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pluralize": { - "version": "8.0.0", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/postcss": { - "version": "8.5.25", - "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", + "version": "8.5.29", + "integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==", "dev": true, "funding": [ { @@ -3279,9 +3089,9 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.16", + "nanoid": "^3.3.19", "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "source-map-js": "^1.2.2" }, "engines": { "node": "^10 || ^12 || >=14" @@ -3311,15 +3121,6 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/process": { - "version": "0.11.10", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/punycode": { "version": "2.3.1", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", @@ -3329,44 +3130,6 @@ "node": ">=6" } }, - "node_modules/quicktype-core": { - "version": "23.2.6", - "integrity": "sha512-asfeSv7BKBNVb9WiYhFRBvBZHcRutPRBwJMxW0pefluK4kkKu4lv0IvZBwFKvw2XygLcL1Rl90zxWDHYgkwCmA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@glideapps/ts-necessities": "2.2.3", - "browser-or-node": "^3.0.0", - "collection-utils": "^1.0.1", - "cross-fetch": "^4.0.0", - "is-url": "^1.2.4", - "js-base64": "^3.7.7", - "lodash": "^4.17.21", - "pako": "^1.0.6", - "pluralize": "^8.0.0", - "readable-stream": "4.5.2", - "unicode-properties": "^1.4.1", - "urijs": "^1.19.1", - "wordwrap": "^1.0.0", - "yaml": "^2.4.1" - } - }, - "node_modules/readable-stream": { - "version": "4.5.2", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", @@ -3396,12 +3159,12 @@ } }, "node_modules/rolldown": { - "version": "1.0.3", - "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", + "version": "1.2.13", + "integrity": "sha512-/5apOkwWxtACcQtZ5WlrAsKS4JwXVThQPJZ3RqIruRY9UG9M8e0VlXN068xum/JRKA4aIUoJ13ghe/kGCvidiQ==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.133.0", + "@oxc-project/types": "=0.153.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -3411,42 +3174,22 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.3", - "@rolldown/binding-darwin-arm64": "1.0.3", - "@rolldown/binding-darwin-x64": "1.0.3", - "@rolldown/binding-freebsd-x64": "1.0.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", - "@rolldown/binding-linux-arm64-gnu": "1.0.3", - "@rolldown/binding-linux-arm64-musl": "1.0.3", - "@rolldown/binding-linux-ppc64-gnu": "1.0.3", - "@rolldown/binding-linux-s390x-gnu": "1.0.3", - "@rolldown/binding-linux-x64-gnu": "1.0.3", - "@rolldown/binding-linux-x64-musl": "1.0.3", - "@rolldown/binding-openharmony-arm64": "1.0.3", - "@rolldown/binding-wasm32-wasi": "1.0.3", - "@rolldown/binding-win32-arm64-msvc": "1.0.3", - "@rolldown/binding-win32-x64-msvc": "1.0.3" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" + "@rolldown/binding-android-arm-eabi": "1.2.13", + "@rolldown/binding-android-arm64": "1.2.13", + "@rolldown/binding-darwin-arm64": "1.2.13", + "@rolldown/binding-darwin-x64": "1.2.13", + "@rolldown/binding-freebsd-x64": "1.2.13", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.13", + "@rolldown/binding-linux-arm64-gnu": "1.2.13", + "@rolldown/binding-linux-arm64-musl": "1.2.13", + "@rolldown/binding-linux-ppc64-gnu": "1.2.13", + "@rolldown/binding-linux-s390x-gnu": "1.2.13", + "@rolldown/binding-linux-x64-gnu": "1.2.13", + "@rolldown/binding-linux-x64-musl": "1.2.13", + "@rolldown/binding-openharmony-arm64": "1.2.13", + "@rolldown/binding-win32-arm64-msvc": "1.2.13", + "@rolldown/binding-win32-x64-msvc": "1.2.13" + } }, "node_modules/semver": { "version": "7.7.3", @@ -3488,8 +3231,8 @@ "license": "ISC" }, "node_modules/source-map-js": { - "version": "1.2.1", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "version": "1.2.2", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -3508,15 +3251,6 @@ "dev": true, "license": "MIT" }, - "node_modules/string_decoder": { - "version": "1.3.0", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", @@ -3557,12 +3291,6 @@ "node": ">=18" } }, - "node_modules/tiny-inflate": { - "version": "1.0.3", - "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", - "dev": true, - "license": "MIT" - }, "node_modules/tinybench": { "version": "2.9.0", "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", @@ -3595,20 +3323,14 @@ } }, "node_modules/tinyrainbow": { - "version": "3.1.0", - "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "version": "3.2.0", + "integrity": "sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==", "dev": true, "license": "MIT", "engines": { "node": ">=14.0.0" } }, - "node_modules/tr46": { - "version": "0.0.3", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true, - "license": "MIT" - }, "node_modules/ts-api-utils": { "version": "2.4.0", "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", @@ -3621,13 +3343,6 @@ "typescript": ">=4.8.4" } }, - "node_modules/tslib": { - "version": "2.8.1", - "integrity": "sha1-YS7+TtI11Wfoq6Xypfq3AoCt6D8=", - "dev": true, - "license": "0BSD", - "optional": true - }, "node_modules/tsx": { "version": "4.22.4", "integrity": "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==", @@ -3677,32 +3392,6 @@ "dev": true, "license": "MIT" }, - "node_modules/unicode-properties": { - "version": "1.4.1", - "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.0", - "unicode-trie": "^2.0.0" - } - }, - "node_modules/unicode-trie": { - "version": "2.0.0", - "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "pako": "^0.2.5", - "tiny-inflate": "^1.0.0" - } - }, - "node_modules/unicode-trie/node_modules/pako": { - "version": "0.2.9", - "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", - "dev": true, - "license": "MIT" - }, "node_modules/uri-js": { "version": "4.4.1", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", @@ -3712,22 +3401,16 @@ "punycode": "^2.1.0" } }, - "node_modules/urijs": { - "version": "1.19.11", - "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==", - "dev": true, - "license": "MIT" - }, "node_modules/vite": { - "version": "8.0.16", - "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", + "version": "8.3.4", + "integrity": "sha512-IftLE8phJhdNRIGNEV4JCCvfNScoZQVfcXaN8TQropWGJtOHcu+iAB9pHcuGvmEb/kddw0LBe/jS8ztdC6xdiQ==", "dev": true, "license": "MIT", "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.15", - "rolldown": "1.0.3", + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.29", + "rolldown": "~1.2.12", "tinyglobby": "^0.2.17" }, "bin": { @@ -3744,7 +3427,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.18", + "@vitejs/devtools": "^0.7.1", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -3796,18 +3479,18 @@ } }, "node_modules/vitest": { - "version": "4.1.8", - "integrity": "sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==", + "version": "4.1.11", + "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.8", - "@vitest/mocker": "4.1.8", - "@vitest/pretty-format": "4.1.8", - "@vitest/runner": "4.1.8", - "@vitest/snapshot": "4.1.8", - "@vitest/spy": "4.1.8", - "@vitest/utils": "4.1.8", + "@vitest/expect": "4.1.11", + "@vitest/mocker": "4.1.11", + "@vitest/pretty-format": "4.1.11", + "@vitest/runner": "4.1.11", + "@vitest/snapshot": "4.1.11", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -3835,12 +3518,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.8", - "@vitest/browser-preview": "4.1.8", - "@vitest/browser-webdriverio": "4.1.8", - "@vitest/coverage-istanbul": "4.1.8", - "@vitest/coverage-v8": "4.1.8", - "@vitest/ui": "4.1.8", + "@vitest/browser-playwright": "4.1.11", + "@vitest/browser-preview": "4.1.11", + "@vitest/browser-webdriverio": "4.1.11", + "@vitest/coverage-istanbul": "4.1.11", + "@vitest/coverage-v8": "4.1.11", + "@vitest/ui": "4.1.11", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -3892,22 +3575,6 @@ "node": ">=14.0.0" } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", @@ -3948,12 +3615,6 @@ "node": ">=0.10.0" } }, - "node_modules/wordwrap": { - "version": "1.0.0", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true, - "license": "MIT" - }, "node_modules/ws": { "version": "8.21.0", "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", @@ -3984,21 +3645,6 @@ "node": ">=18" } }, - "node_modules/yaml": { - "version": "2.9.0", - "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", - "dev": true, - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", diff --git a/nodejs/package.json b/nodejs/package.json index bf0b7222c8..d3b0dd5d52 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -5,7 +5,7 @@ "url": "https://github.com/github/copilot-sdk.git" }, "version": "0.0.0-dev", - "copilotCliVersion": "1.0.95-0", + "copilotCliVersion": "1.0.97-2", "description": "TypeScript SDK for programmatic control of GitHub Copilot CLI via JSON-RPC", "main": "./dist/cjs/index.js", "types": "./dist/index.d.ts", @@ -84,13 +84,12 @@ "json-schema": "^0.4.0", "json-schema-to-typescript": "^15.0.4", "prettier": "^3.8.1", - "quicktype-core": "^23.2.6", "rimraf": "^6.1.2", "semver": "^7.7.3", "tar": "^7.5.22", "tsx": "^4.20.6", "typescript": "^5.0.0", - "vitest": "^4.0.18", + "vitest": "^4.1.11", "ws": "^8.21.0" }, "engines": { diff --git a/nodejs/samples/package-lock.json b/nodejs/samples/package-lock.json index 3215d8b297..8b911dbef4 100644 --- a/nodejs/samples/package-lock.json +++ b/nodejs/samples/package-lock.json @@ -23,6 +23,7 @@ "zod": "4.3.6" }, "devDependencies": { + "@microsoft/agent-host-protocol-v09": "npm:@microsoft/agent-host-protocol@0.9.0", "@platformatic/vfs": "^0.3.0", "@types/node": "^25.2.0", "@types/semver": "7.8.0", @@ -32,16 +33,16 @@ "esbuild": "^0.28.1", "eslint": "^9.0.0", "glob": "^13.0.1", + "hpke-js": "^1.8.0", "json-schema": "^0.4.0", "json-schema-to-typescript": "^15.0.4", "prettier": "^3.8.1", - "quicktype-core": "^23.2.6", "rimraf": "^6.1.2", "semver": "^7.7.3", "tar": "^7.5.22", "tsx": "^4.20.6", "typescript": "^5.0.0", - "vitest": "^4.0.18", + "vitest": "^4.1.11", "ws": "^8.21.0" }, "engines": { @@ -222,31 +223,106 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "../node_modules/@glideapps/ts-necessities": { - "version": "2.2.3", + "../node_modules/@hpke/chacha20poly1305": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@hpke/chacha20poly1305/-/chacha20poly1305-1.8.0.tgz", + "integrity": "sha512-FcBfAQ+Y99vMNJP2yrZ9wpL8V0GOwp1+zMyzvc6alasrBygfFjFm1yeUtyADJCu/27C3Lm5mJzx6u7pwg+cX5w==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@hpke/common": "^1.10.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "../node_modules/@hpke/common": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@hpke/common/-/common-1.10.1.tgz", + "integrity": "sha512-moJwhmtLtuxiUzzNp1jpfBfx8yefKoO9D/RCR9dmwrnc7qjJqId1rEtQz+lSlU5cabX8daToMSx/7HayXOiaFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "../node_modules/@hpke/core": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@hpke/core/-/core-1.9.0.tgz", + "integrity": "sha512-pFxWl1nNJeQCSUFs7+GAblHvXBCjn9EPN65vdKlYQil2aURaRxfGMO6vBKGqm1YHTKwiAxJQNEI70PbSowMP9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@hpke/common": "^1.10.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "../node_modules/@hpke/dhkem-x25519": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@hpke/dhkem-x25519/-/dhkem-x25519-1.8.0.tgz", + "integrity": "sha512-S1MWWkAfu+TFxySgv5+2P3O4Mx/jk7BsoplzQaA1s3sfUJVJ2UsZsSzSsMc+FXJumLXncoJFlO6mK6mDGspfmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@hpke/common": "^1.10.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "../node_modules/@hpke/dhkem-x448": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@hpke/dhkem-x448/-/dhkem-x448-1.8.0.tgz", + "integrity": "sha512-mFfnZfgp4OKkUIS/FKikfUgdnDKRy25ytCKBQiV+N+HbYy3I4v4ZCPBQ69QL+TYmKmCZJeUEnYeS5K+OBRP+Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@hpke/common": "^1.10.0" + }, + "engines": { + "node": ">=16.0.0" + } }, "../node_modules/@humanfs/core": { - "version": "0.19.1", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, "engines": { "node": ">=18.18.0" } }, "../node_modules/@humanfs/node": { - "version": "0.16.7", + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.1", + "@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==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, "../node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "dev": true, @@ -283,7 +359,9 @@ } }, "../node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", "dev": true, "license": "MIT" }, @@ -580,12 +658,22 @@ "url": "https://liberapay.com/Koromix" } }, + "../node_modules/@microsoft/agent-host-protocol-v09": { + "name": "@microsoft/agent-host-protocol", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@microsoft/agent-host-protocol/-/agent-host-protocol-0.9.0.tgz", + "integrity": "sha512-XY6zOqLSqlNianiWbEpZkzKRB2AKwcDPk9yAbp+afxyxhWzTfpJKy+03T5rIipJDnqf45rXiJ5JV/dzUA8JHLw==", + "dev": true, + "license": "MIT" + }, "../node_modules/@oxc-project/types": { - "version": "0.133.0", + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.153.0.tgz", + "integrity": "sha512-jSNrRIyvPVLL5E1z/tS5zLAprosGz/FuI1PWwEOz59j3E4TTTzSpqKb/RDGqJhhW0QHYISGXxq582wGx6T11Tw==", "dev": true, "license": "MIT", "funding": { - "url": "https://github.com/sponsors/Boshen" + "url": "https://github.com/sponsors/oxc-project" } }, "../node_modules/@platformatic/vfs": { @@ -596,8 +684,44 @@ "node": ">= 22" } }, + "../node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.13.tgz", + "integrity": "sha512-Vu/NICrgdRvUOeaJZIyB2FP63s6fO/4ud5l8dXmxDx9yDyn+mN0y+gsvsVviSli+T361Aqo0h46EuMoobrwawA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "../node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.13.tgz", + "integrity": "sha512-6LEoIrGy5vvCd2boNnv2soPSUZj/PRvhp8R/t9KlX+mIZxThIE/ZqxrGXxmuNX0FuvQFvLdZr7S4HrYpYBVhEQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "../node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.3", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.13.tgz", + "integrity": "sha512-CaJSRzlQ6Q+zkDMVA9OkVxBjVoPRCDEE3v0FSJ9kIQBgRjFApA3Si4ijhbNVUji7ZJt+PfO0e46qy4nPInHUPA==", "cpu": [ "arm64" ], @@ -611,18 +735,246 @@ "node": "^20.19.0 || >=22.12.0" } }, + "../node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.13.tgz", + "integrity": "sha512-e4r340I5hxSzJkD3YJBxgzq/rJ3wwxwdLLb2FPt1Zpyem20nrQPT11DDEEkazwxaHumH8wpglsp/Ozgj/wT6mw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "../node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.13.tgz", + "integrity": "sha512-hoBWDZ4/K/mJKVMbsgUd1jfpA2DiOrJ54mpegObU9TBHBBrtX4FLIXJJQRDPZ/2Dn91GvPob+Z2hwKJzf4E5SQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "../node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.13.tgz", + "integrity": "sha512-oF9fZu3vTWnTiVAkDFxU9PJLt1nh20K9RRpuHsR7ZnE2L7XookBbDBVLojCVSLGbvtSlxeUNnjAj6DbNsKyzbg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "../node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.13.tgz", + "integrity": "sha512-DTpb/+jMiqWMPgphn4ksW1zW+XLJ6dJJ9FMvnIk9I291LhCctMPhMYJrP5t9p6C9qa3KXadDdUEZHBaPh3kNxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "../node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.13.tgz", + "integrity": "sha512-xU+Kvv9N2A3SY35zFh1FWWNjxbadE/BKeqnkXe7vScU4GZsw757BlVmDLITZEq8DPpk6m7uoDYkK7P+x/uU5Ow==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "../node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.13.tgz", + "integrity": "sha512-XMk/hnhh25oQ756Lclb1Q11h4ll0xC0Esa5w4Vzp+mAE2S3AGJgeM0SonrIgY8fHhjarb6hcoTuwoFG0VVP2ig==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "../node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.13.tgz", + "integrity": "sha512-U1BoTHYmlywE2n3HUWjzZ+8EQsKr+OwpxYuDWaubx42tUL8QSL7nOqRz/NVHanibbFP3M1Rxuprz/wNcbMKjqw==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "../node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.13.tgz", + "integrity": "sha512-J4cm80Pc6wEwV5AZ42o62sIQgDk+Y7R2s646jIwuvO+4V46QB58N4vVuMPWCBaNgursXLpmUWJV//WQAkPvHBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "../node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.13.tgz", + "integrity": "sha512-W0euZS40U4V6JkmEnVCIX9mcsL6cbXQ4X8c2w+MMOIjFg68TXUW+u3RT+2e1t1pv9mg/EP3pDtK3POXdQ7Rxyw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "../node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.13.tgz", + "integrity": "sha512-do/AF/4n9MRshia3a5aXoy8xM2c6mgfT6D7H/dY6my5z+hfCIR4ab7mk4IRj++nsEQpKy39kd0DQnDq+r692tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "../node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.13.tgz", + "integrity": "sha512-6FtUf2cJZobutJdxnGYX8z0vtbgqU0b2v8vfBhkXb9wp2MppMFtkunE+IaOntyi01rkIQoPW2GHABtfzsC0BRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "../node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.13.tgz", + "integrity": "sha512-UKhFaZTeTcbKrb3iU82m17jVcPEAoqDkCN5MBqgPyZSwp/cQmib21crTS0DdH7T1B7wZRWEI89YQAOJMet5yhg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "../node_modules/@rolldown/pluginutils": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, "license": "MIT" }, "../node_modules/@standard-schema/spec": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "dev": true, "license": "MIT" }, "../node_modules/@types/chai": { "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", "dev": true, "license": "MIT", "dependencies": { @@ -632,6 +984,8 @@ }, "../node_modules/@types/deep-eql": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", "dev": true, "license": "MIT" }, @@ -658,6 +1012,13 @@ "undici-types": "~7.18.0" } }, + "../node_modules/@types/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-1mAINjtQCXXeLkJ9ehXkwOcBpqtLxiVtKhpUf83DdRNdQKV0iXZpaHYqRr7nj+wvxuJzoAmAwXI+sCNMv1CzLQ==", + "dev": true, + "license": "MIT" + }, "../node_modules/@types/ws": { "version": "8.18.1", "dev": true, @@ -878,14 +1239,16 @@ } }, "../node_modules/@vitest/expect": { - "version": "4.1.8", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz", + "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.8", - "@vitest/utils": "4.1.8", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -894,11 +1257,13 @@ } }, "../node_modules/@vitest/mocker": { - "version": "4.1.8", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz", + "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.8", + "@vitest/spy": "4.1.11", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -919,7 +1284,9 @@ } }, "../node_modules/@vitest/pretty-format": { - "version": "4.1.8", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz", + "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==", "dev": true, "license": "MIT", "dependencies": { @@ -930,11 +1297,13 @@ } }, "../node_modules/@vitest/runner": { - "version": "4.1.8", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz", + "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.8", + "@vitest/utils": "4.1.11", "pathe": "^2.0.3" }, "funding": { @@ -942,12 +1311,14 @@ } }, "../node_modules/@vitest/snapshot": { - "version": "4.1.8", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz", + "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.8", - "@vitest/utils": "4.1.8", + "@vitest/pretty-format": "4.1.11", + "@vitest/utils": "4.1.11", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -956,7 +1327,9 @@ } }, "../node_modules/@vitest/spy": { - "version": "4.1.8", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz", + "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", "dev": true, "license": "MIT", "funding": { @@ -964,11 +1337,13 @@ } }, "../node_modules/@vitest/utils": { - "version": "4.1.8", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz", + "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.8", + "@vitest/pretty-format": "4.1.11", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -976,17 +1351,6 @@ "url": "https://opencollective.com/vitest" } }, - "../node_modules/abort-controller": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, "../node_modules/acorn": { "version": "8.15.0", "dev": true, @@ -1028,6 +1392,8 @@ }, "../node_modules/assertion-error": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", "engines": { @@ -1039,27 +1405,10 @@ "dev": true, "license": "MIT" }, - "../node_modules/base64-js": { - "version": "1.5.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "../node_modules/brace-expansion": { - "version": "1.1.16", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -1067,34 +1416,6 @@ "concat-map": "0.0.1" } }, - "../node_modules/browser-or-node": { - "version": "3.0.0", - "dev": true, - "license": "MIT" - }, - "../node_modules/buffer": { - "version": "6.0.3", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "../node_modules/callsites": { "version": "3.1.0", "dev": true, @@ -1104,7 +1425,9 @@ } }, "../node_modules/chai": { - "version": "6.2.2", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.3.0.tgz", + "integrity": "sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==", "dev": true, "license": "MIT", "engines": { @@ -1148,11 +1471,6 @@ "node": ">=18" } }, - "../node_modules/collection-utils": { - "version": "1.0.1", - "dev": true, - "license": "Apache-2.0" - }, "../node_modules/color-convert": { "version": "2.0.1", "dev": true, @@ -1176,17 +1494,11 @@ }, "../node_modules/convert-source-map": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, "license": "MIT" }, - "../node_modules/cross-fetch": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "node-fetch": "^2.7.0" - } - }, "../node_modules/cross-spawn": { "version": "7.0.6", "dev": true, @@ -1223,6 +1535,8 @@ }, "../node_modules/detect-libc": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1458,6 +1772,8 @@ }, "../node_modules/estree-walker": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, "license": "MIT", "dependencies": { @@ -1472,22 +1788,6 @@ "node": ">=0.10.0" } }, - "../node_modules/event-target-shim": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "../node_modules/events": { - "version": "3.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, "../node_modules/expect-type": { "version": "1.3.0", "dev": true, @@ -1628,24 +1928,22 @@ "node": ">=8" } }, - "../node_modules/ieee754": { - "version": "1.2.1", + "../node_modules/hpke-js": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/hpke-js/-/hpke-js-1.8.0.tgz", + "integrity": "sha512-N0PFQlUQsIPS9++nUNn2ZsxTPSv8pONyyrXIGZl0iiherRfS0XW1SvTd+RmepD0TN1S9zzTJkEutMIWWYt0/4w==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" + "license": "MIT", + "dependencies": { + "@hpke/chacha20poly1305": "^1.8.0", + "@hpke/common": "^1.10.0", + "@hpke/core": "^1.9.0", + "@hpke/dhkem-x25519": "^1.8.0", + "@hpke/dhkem-x448": "^1.8.0" + }, + "engines": { + "node": ">=16.0.0" + } }, "../node_modules/ignore": { "version": "7.0.5", @@ -1697,21 +1995,11 @@ "node": ">=0.10.0" } }, - "../node_modules/is-url": { - "version": "1.2.4", - "dev": true, - "license": "MIT" - }, "../node_modules/isexe": { "version": "2.0.0", "dev": true, "license": "ISC" }, - "../node_modules/js-base64": { - "version": "3.7.8", - "dev": true, - "license": "BSD-3-Clause" - }, "../node_modules/js-yaml": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", @@ -1764,107 +2052,333 @@ "json2ts": "dist/src/cli.js" }, "engines": { - "node": ">=16.0.0" + "node": ">=16.0.0" + } + }, + "../node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "../node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "../node_modules/keyv": { + "version": "4.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "../node_modules/koffi": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/koffi/-/koffi-3.2.1.tgz", + "integrity": "sha512-0qE3lZ8jllRqPN4Ob6Ajl7c2bJSJDhQWuKLGP5hIEpHLllJWv1ydHFMhHmHc5p/W9GticKVDbYzZd7TBoQ4CZg==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "url": "https://liberapay.com/Koromix" + }, + "optionalDependencies": { + "@koromix/koffi-android-arm64": "3.2.1", + "@koromix/koffi-android-x64": "3.2.1", + "@koromix/koffi-darwin-arm64": "3.2.1", + "@koromix/koffi-darwin-x64": "3.2.1", + "@koromix/koffi-freebsd-arm64": "3.2.1", + "@koromix/koffi-freebsd-ia32": "3.2.1", + "@koromix/koffi-freebsd-x64": "3.2.1", + "@koromix/koffi-linux-arm": "3.2.1", + "@koromix/koffi-linux-arm64": "3.2.1", + "@koromix/koffi-linux-ia32": "3.2.1", + "@koromix/koffi-linux-loong64": "3.2.1", + "@koromix/koffi-linux-riscv64": "3.2.1", + "@koromix/koffi-linux-x64": "3.2.1", + "@koromix/koffi-openbsd-ia32": "3.2.1", + "@koromix/koffi-openbsd-x64": "3.2.1", + "@koromix/koffi-win32-arm64": "3.2.1", + "@koromix/koffi-win32-ia32": "3.2.1", + "@koromix/koffi-win32-x64": "3.2.1" + } + }, + "../node_modules/levn": { + "version": "0.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "../node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "../node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "../node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "../node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "../node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "../node_modules/json-schema-traverse": { - "version": "0.4.1", + "../node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], "dev": true, - "license": "MIT" + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "../node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", + "../node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "../node_modules/keyv": { - "version": "4.5.4", + "../node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "../node_modules/koffi": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/koffi/-/koffi-3.2.1.tgz", - "integrity": "sha512-0qE3lZ8jllRqPN4Ob6Ajl7c2bJSJDhQWuKLGP5hIEpHLllJWv1ydHFMhHmHc5p/W9GticKVDbYzZd7TBoQ4CZg==", - "hasInstallScript": true, - "license": "MIT", - "funding": { - "url": "https://liberapay.com/Koromix" + "../node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" }, - "optionalDependencies": { - "@koromix/koffi-android-arm64": "3.2.1", - "@koromix/koffi-android-x64": "3.2.1", - "@koromix/koffi-darwin-arm64": "3.2.1", - "@koromix/koffi-darwin-x64": "3.2.1", - "@koromix/koffi-freebsd-arm64": "3.2.1", - "@koromix/koffi-freebsd-ia32": "3.2.1", - "@koromix/koffi-freebsd-x64": "3.2.1", - "@koromix/koffi-linux-arm": "3.2.1", - "@koromix/koffi-linux-arm64": "3.2.1", - "@koromix/koffi-linux-ia32": "3.2.1", - "@koromix/koffi-linux-loong64": "3.2.1", - "@koromix/koffi-linux-riscv64": "3.2.1", - "@koromix/koffi-linux-x64": "3.2.1", - "@koromix/koffi-openbsd-ia32": "3.2.1", - "@koromix/koffi-openbsd-x64": "3.2.1", - "@koromix/koffi-win32-arm64": "3.2.1", - "@koromix/koffi-win32-ia32": "3.2.1", - "@koromix/koffi-win32-x64": "3.2.1" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "../node_modules/levn": { - "version": "0.4.1", + "../node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.8.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "../node_modules/lightningcss": { - "version": "1.32.0", + "../node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" } }, - "../node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", + "../node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", "cpu": [ - "arm64" + "x64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ - "darwin" + "win32" ], "engines": { "node": ">= 12.0.0" @@ -1908,6 +2422,8 @@ }, "../node_modules/magic-string": { "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1937,7 +2453,9 @@ } }, "../node_modules/minimatch/node_modules/brace-expansion": { - "version": "5.0.8", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1980,7 +2498,9 @@ "license": "MIT" }, "../node_modules/nanoid": { - "version": "3.3.17", + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", "dev": true, "funding": [ { @@ -2001,25 +2521,6 @@ "dev": true, "license": "MIT" }, - "../node_modules/node-fetch": { - "version": "2.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "../node_modules/obug": { "version": "2.1.1", "dev": true, @@ -2078,11 +2579,6 @@ "dev": true, "license": "BlueOak-1.0.0" }, - "../node_modules/pako": { - "version": "1.0.11", - "dev": true, - "license": "(MIT AND Zlib)" - }, "../node_modules/parent-module": { "version": "1.0.1", "dev": true, @@ -2127,16 +2623,22 @@ }, "../node_modules/pathe": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, "license": "MIT" }, "../node_modules/picocolors": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, "../node_modules/picomatch": { - "version": "4.0.4", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "engines": { @@ -2146,16 +2648,10 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "../node_modules/pluralize": { - "version": "8.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "../node_modules/postcss": { - "version": "8.5.25", + "version": "8.5.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz", + "integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==", "dev": true, "funding": [ { @@ -2173,9 +2669,9 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.16", + "nanoid": "^3.3.19", "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "source-map-js": "^1.2.2" }, "engines": { "node": "^10 || ^12 || >=14" @@ -2203,14 +2699,6 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "../node_modules/process": { - "version": "0.11.10", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, "../node_modules/punycode": { "version": "2.3.1", "dev": true, @@ -2219,42 +2707,6 @@ "node": ">=6" } }, - "../node_modules/quicktype-core": { - "version": "23.2.6", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@glideapps/ts-necessities": "2.2.3", - "browser-or-node": "^3.0.0", - "collection-utils": "^1.0.1", - "cross-fetch": "^4.0.0", - "is-url": "^1.2.4", - "js-base64": "^3.7.7", - "lodash": "^4.17.21", - "pako": "^1.0.6", - "pluralize": "^8.0.0", - "readable-stream": "4.5.2", - "unicode-properties": "^1.4.1", - "urijs": "^1.19.1", - "wordwrap": "^1.0.0", - "yaml": "^2.4.1" - } - }, - "../node_modules/readable-stream": { - "version": "4.5.2", - "dev": true, - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "../node_modules/resolve-from": { "version": "4.0.0", "dev": true, @@ -2282,11 +2734,13 @@ } }, "../node_modules/rolldown": { - "version": "1.0.3", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.13.tgz", + "integrity": "sha512-/5apOkwWxtACcQtZ5WlrAsKS4JwXVThQPJZ3RqIruRY9UG9M8e0VlXN068xum/JRKA4aIUoJ13ghe/kGCvidiQ==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.133.0", + "@oxc-project/types": "=0.153.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -2296,41 +2750,22 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.3", - "@rolldown/binding-darwin-arm64": "1.0.3", - "@rolldown/binding-darwin-x64": "1.0.3", - "@rolldown/binding-freebsd-x64": "1.0.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", - "@rolldown/binding-linux-arm64-gnu": "1.0.3", - "@rolldown/binding-linux-arm64-musl": "1.0.3", - "@rolldown/binding-linux-ppc64-gnu": "1.0.3", - "@rolldown/binding-linux-s390x-gnu": "1.0.3", - "@rolldown/binding-linux-x64-gnu": "1.0.3", - "@rolldown/binding-linux-x64-musl": "1.0.3", - "@rolldown/binding-openharmony-arm64": "1.0.3", - "@rolldown/binding-wasm32-wasi": "1.0.3", - "@rolldown/binding-win32-arm64-msvc": "1.0.3", - "@rolldown/binding-win32-x64-msvc": "1.0.3" - } - }, - "../node_modules/safe-buffer": { - "version": "5.2.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" + "@rolldown/binding-android-arm-eabi": "1.2.13", + "@rolldown/binding-android-arm64": "1.2.13", + "@rolldown/binding-darwin-arm64": "1.2.13", + "@rolldown/binding-darwin-x64": "1.2.13", + "@rolldown/binding-freebsd-x64": "1.2.13", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.13", + "@rolldown/binding-linux-arm64-gnu": "1.2.13", + "@rolldown/binding-linux-arm64-musl": "1.2.13", + "@rolldown/binding-linux-ppc64-gnu": "1.2.13", + "@rolldown/binding-linux-s390x-gnu": "1.2.13", + "@rolldown/binding-linux-x64-gnu": "1.2.13", + "@rolldown/binding-linux-x64-musl": "1.2.13", + "@rolldown/binding-openharmony-arm64": "1.2.13", + "@rolldown/binding-win32-arm64-msvc": "1.2.13", + "@rolldown/binding-win32-x64-msvc": "1.2.13" + } }, "../node_modules/semver": { "version": "7.7.3", @@ -2368,7 +2803,9 @@ "license": "ISC" }, "../node_modules/source-map-js": { - "version": "1.2.1", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -2385,14 +2822,6 @@ "dev": true, "license": "MIT" }, - "../node_modules/string_decoder": { - "version": "1.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "../node_modules/strip-json-comments": { "version": "3.1.1", "dev": true, @@ -2430,11 +2859,6 @@ "node": ">=18" } }, - "../node_modules/tiny-inflate": { - "version": "1.0.3", - "dev": true, - "license": "MIT" - }, "../node_modules/tinybench": { "version": "2.9.0", "dev": true, @@ -2464,18 +2888,15 @@ } }, "../node_modules/tinyrainbow": { - "version": "3.1.0", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.2.0.tgz", + "integrity": "sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==", "dev": true, "license": "MIT", "engines": { "node": ">=14.0.0" } }, - "../node_modules/tr46": { - "version": "0.0.3", - "dev": true, - "license": "MIT" - }, "../node_modules/ts-api-utils": { "version": "2.4.0", "dev": true, @@ -2532,29 +2953,6 @@ "dev": true, "license": "MIT" }, - "../node_modules/unicode-properties": { - "version": "1.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.0", - "unicode-trie": "^2.0.0" - } - }, - "../node_modules/unicode-trie": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "pako": "^0.2.5", - "tiny-inflate": "^1.0.0" - } - }, - "../node_modules/unicode-trie/node_modules/pako": { - "version": "0.2.9", - "dev": true, - "license": "MIT" - }, "../node_modules/uri-js": { "version": "4.4.1", "dev": true, @@ -2563,20 +2961,17 @@ "punycode": "^2.1.0" } }, - "../node_modules/urijs": { - "version": "1.19.11", - "dev": true, - "license": "MIT" - }, "../node_modules/vite": { - "version": "8.0.16", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.4.tgz", + "integrity": "sha512-IftLE8phJhdNRIGNEV4JCCvfNScoZQVfcXaN8TQropWGJtOHcu+iAB9pHcuGvmEb/kddw0LBe/jS8ztdC6xdiQ==", "dev": true, "license": "MIT", "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.15", - "rolldown": "1.0.3", + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.29", + "rolldown": "~1.2.12", "tinyglobby": "^0.2.17" }, "bin": { @@ -2593,7 +2988,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.18", + "@vitejs/devtools": "^0.7.1", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -2645,17 +3040,19 @@ } }, "../node_modules/vitest": { - "version": "4.1.8", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz", + "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.8", - "@vitest/mocker": "4.1.8", - "@vitest/pretty-format": "4.1.8", - "@vitest/runner": "4.1.8", - "@vitest/snapshot": "4.1.8", - "@vitest/spy": "4.1.8", - "@vitest/utils": "4.1.8", + "@vitest/expect": "4.1.11", + "@vitest/mocker": "4.1.11", + "@vitest/pretty-format": "4.1.11", + "@vitest/runner": "4.1.11", + "@vitest/snapshot": "4.1.11", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -2683,12 +3080,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.8", - "@vitest/browser-preview": "4.1.8", - "@vitest/browser-webdriverio": "4.1.8", - "@vitest/coverage-istanbul": "4.1.8", - "@vitest/coverage-v8": "4.1.8", - "@vitest/ui": "4.1.8", + "@vitest/browser-playwright": "4.1.11", + "@vitest/browser-preview": "4.1.11", + "@vitest/browser-webdriverio": "4.1.11", + "@vitest/coverage-istanbul": "4.1.11", + "@vitest/coverage-v8": "4.1.11", + "@vitest/ui": "4.1.11", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -2739,20 +3136,6 @@ "node": ">=14.0.0" } }, - "../node_modules/webidl-conversions": { - "version": "3.0.1", - "dev": true, - "license": "BSD-2-Clause" - }, - "../node_modules/whatwg-url": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "../node_modules/which": { "version": "2.0.2", "dev": true, @@ -2790,11 +3173,6 @@ "node": ">=0.10.0" } }, - "../node_modules/wordwrap": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, "../node_modules/ws": { "version": "8.21.0", "dev": true, @@ -2823,20 +3201,6 @@ "node": ">=18" } }, - "../node_modules/yaml": { - "version": "2.9.0", - "dev": true, - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, "../node_modules/yocto-queue": { "version": "0.1.0", "dev": true, diff --git a/nodejs/src/cliVersion.ts b/nodejs/src/cliVersion.ts index ff5a2eeb37..58d20a7d2c 100644 --- a/nodejs/src/cliVersion.ts +++ b/nodejs/src/cliVersion.ts @@ -1,3 +1,3 @@ -export const COPILOT_CLI_VERSION = "1.0.95-0"; +export const COPILOT_CLI_VERSION = "1.0.97-2"; export const COPILOT_CLI_USE_NPM_PACKAGE = false; diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index 2f00114da9..464a525427 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -1790,6 +1790,7 @@ export class CopilotClient { model: config.model, allowedModels: config.allowedModels, sessionId: localSessionId, + name: config.name, clientName: config.clientName, reasoningEffort: config.reasoningEffort, reasoningSummary: config.reasoningSummary, @@ -1806,6 +1807,7 @@ export class CopilotClient { isTerminal: tool.isTerminal, })), toolSearch: config.toolSearch, + imageGeneration: config.imageGeneration, canvases: config.canvases?.map((canvas) => canvas.declaration), requestCanvasRenderer: config.requestCanvasRenderer, requestExtensions: config.requestExtensions, @@ -1871,6 +1873,8 @@ export class CopilotClient { enableSkills: config.enableSkills, skillDirectories: config.skillDirectories, ...(config.skillProvider ? { hasSkillProvider: true } : {}), + agentDirectories: config.agentDirectories, + hookDirectories: config.hookDirectories, pluginDirectories: config.pluginDirectories, instructionDirectories: config.instructionDirectories, disabledSkills: config.disabledSkills, @@ -2119,6 +2123,7 @@ export class CopilotClient { isTerminal: tool.isTerminal, })), toolSearch: config.toolSearch, + imageGeneration: config.imageGeneration, canvases: config.canvases?.map((canvas) => canvas.declaration), workflows: workflows?.map((workflow) => workflow.meta), requestCanvasRenderer: config.requestCanvasRenderer, @@ -2176,6 +2181,8 @@ export class CopilotClient { agent: config.agent, skillDirectories: config.skillDirectories, ...(config.skillProvider ? { hasSkillProvider: true } : {}), + agentDirectories: config.agentDirectories, + hookDirectories: config.hookDirectories, pluginDirectories: config.pluginDirectories, instructionDirectories: config.instructionDirectories, disabledSkills: config.disabledSkills, @@ -3253,6 +3260,8 @@ export class CopilotClient { /** Starts the in-process FFI runtime with SDK-managed typed options. */ private async startInProcessFfi(): Promise { const explicitEntrypoint = this.resolvedEnv.COPILOT_CLI_PATH; + const cliLoginSyncIntegrationId = + this.resolvedEnv.GITHUB_COPILOT_INTEGRATION_ID?.trim() || undefined; let runtimeLibrary: string; if (explicitEntrypoint) { const entrypointDirectory = dirname(resolve(explicitEntrypoint)); @@ -3266,7 +3275,8 @@ export class CopilotClient { "runtime.node" ); } else { - runtimeLibrary = join(dirname(await getBundledRuntimePath()), "runtime.node"); + const bundledRuntimePath = await getBundledRuntimePath(); + runtimeLibrary = join(dirname(bundledRuntimePath), "runtime.node"); } // Load the FFI host lazily so the native `koffi` addon (and its // platform-specific `koffi.node`) is only loaded on the in-process path; @@ -3301,6 +3311,9 @@ export class CopilotClient { if (this.options.enableRemoteSessions) { args.push("--remote"); } + if (cliLoginSyncIntegrationId) { + args.push(`--cli-login-sync-integration-id=${cliLoginSyncIntegrationId}`); + } const host = FfiRuntimeHost.create(runtimeLibrary, explicitEntrypoint, environment, args); this.ffiHost = host; diff --git a/nodejs/src/extension.ts b/nodejs/src/extension.ts index 99a1c0a3be..f6adc32745 100644 --- a/nodejs/src/extension.ts +++ b/nodejs/src/extension.ts @@ -24,7 +24,7 @@ export { export type JoinSessionConfig = Omit< ResumeSessionConfig, - "onPermissionRequest" | "extensionSdkPath" | "skillProvider" + "onPermissionRequest" | "extensionSdkPath" | "skillProvider" | "imageGeneration" > & { onPermissionRequest?: PermissionHandler; /** @@ -130,15 +130,19 @@ export async function joinSession(config: JoinSessionConfig = {}): Promise/settings/.json`; path separators, `.` and `..` are rejected. Omit it, or pass an empty string, to use the main settings document. + */ + subDir?: string; +} +/** + * The stored value of the user setting after the merge. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsUpdateResult". + */ +/** @experimental */ +/** @internal */ +export interface UserSettingsUpdateResult { + /** + * The key's stored value after the write: the merged object when both the stored value and the supplied value are objects, otherwise the supplied value. + */ + value: JsonValue; +} +/** + * A single top-level setting subtree to validate. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsValidateRequest". + */ +/** @experimental */ +export interface UserSettingsValidateRequest { + /** + * Top-level setting name to validate. + */ + topKey: string; + /** + * Value to validate. Omitted when `delete` is true. + */ + value?: JsonValue; + /** + * True to validate a removal of this key. The current settings file is not read. + */ + delete?: boolean; +} +/** + * Outcome of validating one top-level setting subtree. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsValidateResult". + */ +/** @experimental */ +export interface UserSettingsValidateResult { + /** + * True when the subtree is valid. + */ + ok: boolean; + /** + * Problems found, empty when `ok` is true. + */ + issues?: UserSettingsValidationIssue[]; +} +/** + * The whole content to store in the user settings file. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsWriteAllRequest". + */ +/** @experimental */ +export interface UserSettingsWriteAllRequest { + /** + * The settings object to store. It replaces the file's entire content, so a key absent here is removed. + */ + settings: JsonValue; +} /** * Current sharing status and shareable GitHub URL for a session. * @@ -31281,6 +34286,102 @@ export interface WorkspacesWriteAutopilotObjectiveResult { */ operation: string; } +/** + * The effective sandbox settings, with every unset value resolved to its default. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxSettingsApplyDefaultsResult". + */ +/** @experimental */ +export interface SandboxSettingsApplyDefaultsResult { + /** + * One resolved sandbox setting's value, carried verbatim. + */ + [k: string]: JsonValue | undefined; +} +/** + * The stored user settings document, keyed by setting name. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsLoadForConfigDirResult". + */ +/** @experimental */ +export interface UserSettingsLoadForConfigDirResult { + /** + * One setting's stored value, carried verbatim. + */ + [k: string]: JsonValue | undefined; +} +/** + * The effective runtime settings, keyed by setting name. Keys this runtime does not recognize are preserved rather than dropped, so a caller reading a document written by a newer runtime keeps every value. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsBuildRuntimeResult". + */ +/** @experimental */ +export interface UserSettingsBuildRuntimeResult { + /** + * One resolved setting's effective value, carried verbatim. + */ + [k: string]: JsonValue | undefined; +} +/** + * The resolved gates, keyed by flag name. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "FeatureFlagsResolveStartupGatesResult". + */ +/** @experimental */ +export interface FeatureFlagsResolveStartupGatesResult { + /** + * Whether the gate is on for this installation. + */ + [k: string]: boolean | undefined; +} +/** + * The resolved feature flags, keyed by flag name. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "FeatureFlagsResolveResult". + */ +/** @experimental */ +export interface FeatureFlagsResolveResult { + /** + * Whether the flag is enabled for this population. + */ + [k: string]: boolean | undefined; +} +/** + * Ordered managed permission verdicts and the supplied policy's fail-closed posture. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsPermissionsEvaluateResult". + */ +/** @experimental */ +export interface ManagedSettingsPermissionsEvaluateResult { + /** + * One verdict per input operation, in the same order, including duplicates. + */ + results: ManagedPermissionEvaluation[]; + /** + * Managed permission policy could not be determined; every operation is denied. + */ + failClosed: boolean; +} +/** + * Managed policy context and ordered operations for pure, sessionless evaluation. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsPermissionsEvaluateRequest". + */ +/** @experimental */ +export interface ManagedSettingsPermissionsEvaluateRequest { + context: ManagedPermissionsContext; + /** + * Operations to evaluate, preserving input order and duplicates. An empty batch is valid. + */ + operations: ManagedPermissionOperation[]; +} /** * The resident session the application has materialized on its own connection. * @@ -31930,128 +35031,26 @@ export interface InstallationsConfirmRequest { /** Create typed server-scoped RPC methods (no session required). */ export function createServerRpc(connection: MessageConnection) { return { - /** @experimental */ - environments: { - /** - * Lists GitHub Mission Control environments visible to the authenticated identity. Does not require a running host and excludes host relay credentials. - * - * @param params Optional discovery filters supported by GitHub Mission Control. - * - * @returns Environments visible to the authenticated caller and matching the supplied filters. - */ - list: async (params: EnvironmentsListRequest): Promise => - connection.sendRequest("environments.list", params), - /** - * Gets safe discovery information for a GitHub Mission Control environment without requiring a running host. - * - * @param params Identify a Mission Control environment to retrieve. - * - * @returns Safe discovery information for the requested environment. - */ - get: async (params: EnvironmentsGetRequest): Promise => - connection.sendRequest("environments.get", params), - /** - * Deletes a user-managed GitHub Mission Control environment. GitHub-managed environments cannot be deleted. Does not stop a running host, which may register again. - * - * @param params Identify a user-managed Mission Control environment to delete. - * - * @returns Acknowledgement that the requested environment was deleted. - */ - delete: async (params: EnvironmentsDeleteRequest): Promise => - connection.sendRequest("environments.delete", params), - }, - /** @experimental */ - host: { - /** - * Publishes an attached resident session for this listener's lifetime without copying it. - * - * @param params Publishes a resident session attached to the listener's owning connection. - * - * @returns The existing runtime identity and its resource on the listener. - */ - publishSession: async (params: HostPublishSessionRequest): Promise => - connection.sendRequest("host.publishSession", params), - /** - * Reads the complete advertised live and dormant session catalog of an existing host owned by this connection. Does not start hosting, subscribe to sessions, or guarantee resumability. - * - * @param params Reads a running host's catalog or the runtime's saved default catalog. - * - * @returns The complete live and dormant advertised catalog, without subscribing to sessions. - */ - listSessions: async (params: HostListSessionsRequest): Promise => - connection.sendRequest("host.listSessions", params), - /** - * Starts a connection-owned AHP host with explicit localServer and/or githubEnvironment transports as a supervised SDK participant. - * - * @param params Starts a supervised AHP host with at least one explicitly selected transport. - * - * @returns Listener readiness, returned only after binding and the supervised participant's SDK handshake. - */ - start: async (params: HostStartRequest): Promise => - connection.sendRequest("host.start", params), - /** - * Stops a listener owned by this SDK connection and joins its cleanup without deleting sessions. - * - * @param params Stops a connection-owned listener and joins its teardown. - * - * @returns Empty acknowledgement for a completed host lifecycle operation. - */ - dispose: async (params: HostDisposeRequest): Promise => - connection.sendRequest("host.dispose", params), - }, - /** - * Checks server responsiveness and returns protocol information. - * - * @param params Optional message to echo back to the caller. - * - * @returns Server liveness response, including the echoed message, current server timestamp, and protocol version. - * - * @experimental - */ - ping: async (params: PingRequest): Promise => - connection.sendRequest("ping", params), - /** @experimental */ - hooks: { - /** - * Discovers hook actions enabled under server-side discovery settings from user, repository, plugin, and managed-policy sources. - * - * @param params Optional project paths and host-exclusion behavior for server-scoped hook discovery. - * - * @returns Server-discovered hook actions and partial-load diagnostics from user, repository, plugin, and managed-policy sources. Concrete sessions may include additional session-specific hook sources. - */ - discover: async (params: HooksDiscoverRequest): Promise => - connection.sendRequest("hooks.discover", params), - }, - /** @experimental */ - models: { - /** - * Lists Copilot models available to the authenticated user. - * - * @param params Optional opaque account selection or compatibility GitHub token used to list models. - * - * @returns List of Copilot models available to the resolved user, including capabilities and billing metadata. - */ - list: async (params: ModelsListRequest): Promise => - connection.sendRequest("models.list", params), - /** - * Returns the running runtime's complete catalog of well-known built-in model IDs without authentication or network access. - * - * @returns The running runtime's complete catalog of well-known built-in model IDs, including supported models and additional IDs with built-in metadata. - */ - getBuiltInCatalog: async (): Promise => - connection.sendRequest("models.getBuiltInCatalog", {}), - }, /** @experimental */ sandbox: { - /** - * Reports whether the host running this runtime can run the command sandbox, without starting a session or spawning a sandboxed command. - * - * @returns Whether the host running this runtime can run the command sandbox. The runtime checks `supported` once per process. A capability answer can change while the process runs, for example after the user installs a missing package. - */ - getHostSupport: async (): Promise => - connection.sendRequest("sandbox.getHostSupport", {}), /** @experimental */ proxyCa: { + /** + * Reports whether this runtime process can offer installing the sandbox credential-proxy certificate authority into OS trust. + * + * @returns True when this process can offer installing the sandbox credential-proxy certificate authority into OS trust. + */ + checkInstallationSupport: async (): Promise => + connection.sendRequest("sandbox.proxyCa.checkInstallationSupport", {}), + /** + * Reports whether this sandbox configuration uses the credential proxy and can need certificate-authority setup. + * + * @param params Identifies the credential hosts that the persistent certificate authority of the sandbox credential proxy must cover. The runtime always adds the hosts from the saved user settings. + * + * @returns True when the sandbox configuration uses the credential proxy and can need certificate-authority setup. + */ + checkRelevance: async (params: SandboxProxyCaRequest): Promise => + connection.sendRequest("sandbox.proxyCa.checkRelevance", params), /** * Reports whether the persistent certificate authority of the sandbox credential proxy exists, whether OS trust includes it, and whether it must be rotated. Changes nothing. * @@ -32096,6 +35095,205 @@ export function createServerRpc(connection: MessageConnection) { remove: async (): Promise => connection.sendRequest("sandbox.proxyCa.remove", {}), }, + /** + * Reports whether the host running this runtime can run the command sandbox, without starting a session or spawning a sandboxed command. + * + * @returns Whether the host running this runtime can run the command sandbox. The runtime checks `supported` once per process. A capability answer can change while the process runs, for example after the user installs a missing package. + */ + getHostSupport: async (): Promise => + connection.sendRequest("sandbox.getHostSupport", {}), + }, + /** @experimental */ + user: { + /** @experimental */ + settings: { + /** + * Lists every known user setting from settings.json, each with its effective value, its default, and whether it is at the default — so settings the user has never set still appear with their default value. Does not include repository- or enterprise-managed overrides that the runtime layers on top at session time. + * + * @returns Per-key metadata for every known user setting in settings.json, including settings left at their default. Excludes repository- and enterprise-managed overrides. + */ + get: async (): Promise => + connection.sendRequest("user.settings.get", {}), + /** + * Writes one or more user settings to settings.json, replacing each provided top-level key. A key whose value is null is removed. + * + * @param params Partial user settings to write to settings.json. Each top-level key is written individually, replacing the existing value; a key whose value is null is removed. + */ + set: async (params: UserSettingsSetRequest): Promise => + connection.sendRequest("user.settings.set", params), + }, + }, + /** @experimental */ + managedSettings: { + /** + * Discovers device-managed settings from production MDM and managed-file sources, validates them against the runtime-owned managed-settings schema, and returns the canonical JSON without requiring a session. `managedSettings.resolve` returns the same device settings together with the account's server policy. + * + * @returns Validated device-managed settings discovered before a session exists. + */ + read: async (): Promise => + connection.sendRequest("managedSettings.read", {}), + /** + * Force-refreshes enterprise managed settings for every account: wipes the persistent server-policy cache (the whole `/managed-settings` directory) and drops this runtime process's in-memory retained server policy. It does not itself fetch policy — the effect is that the next time a session resolves managed settings for an account, that resolution re-fetches the account's org policy from the network instead of serving a cached response. Note that `managedSettings.read` returns only device/MDM settings and never triggers the account server-policy fetch, so a host implementing "sync account policy" should call `managedSettings.resolve` or start a fresh session resolution rather than treat a subsequent `managedSettings.read` as the refreshed org policy. Mirrors the invalidation a sign-out performs, broadened from the one signing-out account to all of them; device/MDM layers describe the machine, not the account, and are left untouched. Rejects if the on-disk cache cannot be removed. + */ + clearCache: async (): Promise => + connection.sendRequest("managedSettings.clearCache", {}), + /** + * Resolves the effective enterprise managed settings without a session, from the device channel and, when an account is available, the account's server policy through the same per-account cache sessions use. A cached server policy less than an hour old is used without a fetch; otherwise the policy is fetched, and when the fetch fails a cached policy up to 24 hours old is used instead, unless `forceRemoteSettingsRefresh` requires a live fetch. With no account requested or signed in, it reports device policy only; signing out removes the account's cached policy. It can fetch server policy over the network when the cache is stale, so call it off latency-critical paths such as startup rather than before listing models. The policy helper is not run. `layers` lists each channel's document before merging, and `values` and `meta` carry typed effective values and their lock state for the keys typed so far. + * + * @param params Optional opaque account selection or GitHub token whose managed settings are resolved. + * + * @returns Effective enterprise managed settings for an account, resolved without a session. + */ + resolve: async (params: ManagedSettingsResolveRequest): Promise => + connection.sendRequest("managedSettings.resolve", params), + /** @experimental */ + permissions: { + /** + * Pure, sessionless evaluation of operations against a supplied permissionsContext. Performs no policy discovery, I/O, prompting, navigation, permission grants, or sandbox changes. Obtain the context from managedSettings.resolve or session.managedSettings.get, or reuse an in-memory context. Results preserve input order and duplicates. Consumers must block deny, obtain approval for ask, and use their normal permission flow for unmanaged. Unknown policy returns failClosed with every operation denied. This is not a substitute for user permissions or other security controls. + * + * @param params Managed policy context and ordered operations for pure, sessionless evaluation. + * + * @returns Ordered managed permission verdicts and the supplied policy's fail-closed posture. + */ + evaluate: async (params: ManagedSettingsPermissionsEvaluateRequest): Promise => + connection.sendRequest("managedSettings.permissions.evaluate", params), + }, + /** + * Returns the managed-settings authoring JSON schema with descriptive `x-composition` annotations aligned with the shared settings-engine vocabulary. These annotations are not a complete runtime composition contract: model, effortLevel, and contextTier remain coupled. Use `managedSettings.compose` for the runtime's effective result. Performs no I/O. + * + * @returns The authoring JSON schema for managed settings recognized by this runtime. + */ + schema: async (): Promise => + connection.sendRequest("managedSettings.schema", {}), + /** + * Validates a candidate managed-settings document the way the runtime validates delivered policy, without applying it. Reports errors that would reject the document, warnings for content the runtime ignores, and the canonical document it would apply. Document text nested more than 64 levels deep is rejected. Performs no I/O. + * + * @param params A candidate managed-settings document to validate without applying it. + * + * @returns Result of validating a managed-settings document. + */ + validate: async (params: ManagedSettingsValidateRequest): Promise => + connection.sendRequest("managedSettings.validate", params), + /** + * Merges candidate managed-settings documents for the device, server, and policy-helper channels into the effective settings the runtime would enforce on this host, using the same precedence and composition rules as live resolution, without applying them. Like live resolution, a server's advisory sandbox force-enable is declined on a host that cannot run the sandbox. Does not fetch policy or read policy files, but may perform blocking OS or subprocess probes for sandbox support. Preview documents are limited to 1 MiB and 64 levels of nesting. + * + * @param params Candidate managed-settings documents to merge without applying them. + * + * @returns The effective managed settings the runtime would enforce for the given documents, in the same shape `managedSettings.resolve` returns. + */ + compose: async (params: ManagedSettingsComposeRequest): Promise => + connection.sendRequest("managedSettings.compose", params), + }, + /** @experimental */ + environments: { + /** + * Lists GitHub Mission Control environments visible to the authenticated identity. Does not require a running host and excludes host relay credentials. + * + * @param params Optional discovery filters supported by GitHub Mission Control. + * + * @returns Environments visible to the authenticated caller and matching the supplied filters. + */ + list: async (params: EnvironmentsListRequest): Promise => + connection.sendRequest("environments.list", params), + /** + * Gets safe discovery information for a GitHub Mission Control environment without requiring a running host. + * + * @param params Identify a Mission Control environment to retrieve. + * + * @returns Safe discovery information for the requested environment. + */ + get: async (params: EnvironmentsGetRequest): Promise => + connection.sendRequest("environments.get", params), + /** + * Deletes a user-managed GitHub Mission Control environment. GitHub-managed environments cannot be deleted. Does not stop a running host, which may register again. + * + * @param params Identify a user-managed Mission Control environment to delete. + * + * @returns Acknowledgement that the requested environment was deleted. + */ + delete: async (params: EnvironmentsDeleteRequest): Promise => + connection.sendRequest("environments.delete", params), + }, + /** @experimental */ + host: { + /** + * Publishes an attached resident session for this listener's lifetime without copying it. + * + * @param params Publishes a resident session attached to the listener's owning connection. + * + * @returns The existing runtime identity and its resource on the listener. + */ + publishSession: async (params: HostPublishSessionRequest): Promise => + connection.sendRequest("host.publishSession", params), + /** + * Reads the complete advertised live and dormant session catalog of an existing host owned by this connection. Does not start hosting, subscribe to sessions, or guarantee resumability. + * + * @param params Reads a running host's catalog or the runtime's saved default catalog. + * + * @returns The complete live and dormant advertised catalog, without subscribing to sessions. + */ + listSessions: async (params: HostListSessionsRequest): Promise => + connection.sendRequest("host.listSessions", params), + /** + * Starts a connection-owned AHP host with explicit localServer and/or githubEnvironment transports as a supervised SDK participant. + * + * @param params Starts a supervised AHP host with at least one explicitly selected transport. + * + * @returns Listener readiness, returned only after binding and the supervised participant's SDK handshake. + */ + start: async (params: HostStartRequest): Promise => + connection.sendRequest("host.start", params), + /** + * Stops a listener owned by this SDK connection and joins its cleanup without deleting sessions. + * + * @param params Stops a connection-owned listener and joins its teardown. + * + * @returns Empty acknowledgement for a completed host lifecycle operation. + */ + dispose: async (params: HostDisposeRequest): Promise => + connection.sendRequest("host.dispose", params), + }, + /** + * Checks server responsiveness and returns protocol information. + * + * @param params Optional message to echo back to the caller. + * + * @returns Server liveness response, including the echoed message, current server timestamp, and protocol version. + * + * @experimental + */ + ping: async (params: PingRequest): Promise => + connection.sendRequest("ping", params), + /** @experimental */ + hooks: { + /** + * Discovers hook actions enabled under server-side discovery settings from user, repository, plugin, and managed-policy sources. + * + * @param params Optional project paths and host-exclusion behavior for server-scoped hook discovery. + * + * @returns Server-discovered hook actions and partial-load diagnostics from user, repository, plugin, and managed-policy sources. Concrete sessions may include additional session-specific hook sources. + */ + discover: async (params: HooksDiscoverRequest): Promise => + connection.sendRequest("hooks.discover", params), + }, + /** @experimental */ + models: { + /** + * Lists Copilot models available to the authenticated user. + * + * @param params Optional opaque account selection or compatibility GitHub token used to list models. + * + * @returns List of Copilot models available to the resolved user, including capabilities and billing metadata. + */ + list: async (params: ModelsListRequest): Promise => + connection.sendRequest("models.list", params), + /** + * Returns the running runtime's complete catalog of well-known built-in model IDs without authentication or network access. + * + * @returns The running runtime's complete catalog of well-known built-in model IDs, including supported models and additional IDs with built-in metadata. + */ + getBuiltInCatalog: async (): Promise => + connection.sendRequest("models.getBuiltInCatalog", {}), }, /** @experimental */ tools: { @@ -32336,6 +35534,27 @@ export function createServerRpc(connection: MessageConnection) { connection.sendRequest("extensions.disable", params), }, /** @experimental */ + instructions: { + /** + * Discovers instruction sources across user, repository, and plugin sources. + * + * @param params Optional project paths to include in instruction discovery. + * + * @returns Instruction sources discovered across user, repository, and plugin sources. + */ + discover: async (params: InstructionsDiscoverRequest): Promise => + connection.sendRequest("instructions.discover", params), + /** + * 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. + * + * @param params Optional project paths to include when enumerating instruction discovery targets. + * + * @returns Canonical files and directories where custom instructions can be created so the runtime will recognize them. + */ + getDiscoveryPaths: async (params: InstructionsGetDiscoveryPathsRequest): Promise => + connection.sendRequest("instructions.getDiscoveryPaths", params), + }, + /** @experimental */ skills: { /** * Plans installation of a verified Agent Finder Skill candidate without writing files. The returned review is safe to present to a user and installing always leaves the Skill disabled until separately enabled. @@ -32619,27 +35838,6 @@ export function createServerRpc(connection: MessageConnection) { connection.sendRequest("agents.getDiscoveryPaths", params), }, /** @experimental */ - instructions: { - /** - * Discovers instruction sources across user, repository, and plugin sources. - * - * @param params Optional project paths to include in instruction discovery. - * - * @returns Instruction sources discovered across user, repository, and plugin sources. - */ - discover: async (params: InstructionsDiscoverRequest): Promise => - connection.sendRequest("instructions.discover", params), - /** - * 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. - * - * @param params Optional project paths to include when enumerating instruction discovery targets. - * - * @returns Canonical files and directories where custom instructions can be created so the runtime will recognize them. - */ - getDiscoveryPaths: async (params: InstructionsGetDiscoveryPathsRequest): Promise => - connection.sendRequest("instructions.getDiscoveryPaths", params), - }, - /** @experimental */ commands: { /** * Lists the well-known built-in slash commands that work as the first message in a new session (e.g. /plan, /env), without requiring an active session. Commands that depend on session state, authentication, or a synced session are omitted. @@ -32650,75 +35848,6 @@ export function createServerRpc(connection: MessageConnection) { connection.sendRequest("commands.list", {}), }, /** @experimental */ - user: { - /** @experimental */ - settings: { - /** - * Lists every known user setting from settings.json, each with its effective value, its default, and whether it is at the default — so settings the user has never set still appear with their default value. Does not include repository- or enterprise-managed overrides that the runtime layers on top at session time. - * - * @returns Per-key metadata for every known user setting in settings.json, including settings left at their default. Excludes repository- and enterprise-managed overrides. - */ - get: async (): Promise => - connection.sendRequest("user.settings.get", {}), - /** - * Writes one or more user settings to settings.json, replacing each provided top-level key. A key whose value is null is removed. - * - * @param params Partial user settings to write to settings.json. Each top-level key is written individually, replacing the existing value; a key whose value is null is removed. - */ - set: async (params: UserSettingsSetRequest): Promise => - connection.sendRequest("user.settings.set", params), - }, - }, - /** @experimental */ - managedSettings: { - /** - * Discovers device-managed settings from production MDM and managed-file sources, validates them against the runtime-owned managed-settings schema, and returns the canonical JSON without requiring a session. `managedSettings.resolve` returns the same device settings together with the account's server policy. - * - * @returns Validated device-managed settings discovered before a session exists. - */ - read: async (): Promise => - connection.sendRequest("managedSettings.read", {}), - /** - * Force-refreshes enterprise managed settings for every account: wipes the persistent server-policy cache (the whole `/managed-settings` directory) and drops this runtime process's in-memory retained server policy. It does not itself fetch policy — the effect is that the next time a session resolves managed settings for an account, that resolution re-fetches the account's org policy from the network instead of serving a cached response. Note that `managedSettings.read` returns only device/MDM settings and never triggers the account server-policy fetch, so a host implementing "sync account policy" should call `managedSettings.resolve` or start a fresh session resolution rather than treat a subsequent `managedSettings.read` as the refreshed org policy. Mirrors the invalidation a sign-out performs, broadened from the one signing-out account to all of them; device/MDM layers describe the machine, not the account, and are left untouched. Rejects if the on-disk cache cannot be removed. - */ - clearCache: async (): Promise => - connection.sendRequest("managedSettings.clearCache", {}), - /** - * Resolves the effective enterprise managed settings without a session, from the device channel and, when an account is available, the account's server policy through the same per-account cache sessions use. A cached server policy less than an hour old is used without a fetch; otherwise the policy is fetched, and when the fetch fails a cached policy up to 24 hours old is used instead, unless `forceRemoteSettingsRefresh` requires a live fetch. With no account requested or signed in, it reports device policy only; signing out removes the account's cached policy. It can fetch server policy over the network when the cache is stale, so call it off latency-critical paths such as startup rather than before listing models. The policy helper is not run. `layers` lists each channel's document before merging, and `values` and `meta` carry typed effective values and their lock state for the keys typed so far. - * - * @param params Optional opaque account selection or GitHub token whose managed settings are resolved. - * - * @returns Effective enterprise managed settings for an account, resolved without a session. - */ - resolve: async (params: ManagedSettingsResolveRequest): Promise => - connection.sendRequest("managedSettings.resolve", params), - /** - * Returns the managed-settings authoring JSON schema with descriptive `x-composition` annotations aligned with the shared settings-engine vocabulary. These annotations are not a complete runtime composition contract: model, effortLevel, and contextTier remain coupled. Use `managedSettings.compose` for the runtime's effective result. Performs no I/O. - * - * @returns The authoring JSON schema for managed settings recognized by this runtime. - */ - schema: async (): Promise => - connection.sendRequest("managedSettings.schema", {}), - /** - * Validates a candidate managed-settings document the way the runtime validates delivered policy, without applying it. Reports errors that would reject the document, warnings for content the runtime ignores, and the canonical document it would apply. Document text nested more than 64 levels deep is rejected. Performs no I/O. - * - * @param params A candidate managed-settings document to validate without applying it. - * - * @returns Result of validating a managed-settings document. - */ - validate: async (params: ManagedSettingsValidateRequest): Promise => - connection.sendRequest("managedSettings.validate", params), - /** - * Merges candidate managed-settings documents for the device, server, and policy-helper channels into the effective settings the runtime would enforce on this host, using the same precedence and composition rules as live resolution, without applying them. Like live resolution, a server's advisory sandbox force-enable is declined on a host that cannot run the sandbox. Does not fetch policy or read policy files, but may perform blocking OS or subprocess probes for sandbox support. Preview documents are limited to 1 MiB and 64 levels of nesting. - * - * @param params Candidate managed-settings documents to merge without applying them. - * - * @returns The effective managed settings the runtime would enforce for the given documents, in the same shape `managedSettings.resolve` returns. - */ - compose: async (params: ManagedSettingsComposeRequest): Promise => - connection.sendRequest("managedSettings.compose", params), - }, - /** @experimental */ runtime: { /** * Gracefully shuts down an SDK-owned runtime. The response is sent only after cleanup completes; callers may then terminate the owned runtime process. @@ -33047,6 +36176,554 @@ export function createServerRpc(connection: MessageConnection) { */ export function createInternalServerRpc(connection: MessageConnection) { return { + /** @experimental */ + sandbox: { + /** @experimental */ + proxySecret: { + /** + * Reports where this runtime keeps the sandbox proxy password: the credential store directory and the entry identifier within it. Both are derived from the configuration directory this runtime was started with, so a host does not resolve them itself. Read this to recognize a `${secret:…}` value in stored sandbox settings as this runtime's own proxy password rather than a reference the user wrote by hand. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract. + * + * @returns Where this runtime keeps the sandbox proxy password. + */ + getLocation: async (): Promise => + connection.sendRequest("sandbox.proxySecret.getLocation", {}), + /** + * Reads the stored sandbox proxy password from the credential store `sandbox.proxySecret.getLocation` names, preferring the OS keychain and falling back to the on-disk store. Reports an absent value rather than failing when no entry exists. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract. + * + * @returns The stored sandbox proxy password, if one is present. + */ + get: async (): Promise => + connection.sendRequest("sandbox.proxySecret.get", {}), + /** + * Stores the sandbox proxy password in the credential store `sandbox.proxySecret.getLocation` names, replacing any existing entry. Reports whether the OS keychain accepted it or it fell back to the on-disk store. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract. + * + * @param params The sandbox proxy password to store. + * + * @returns Where the sandbox proxy password was stored. + */ + save: async (params: SandboxProxySecretSaveRequest): Promise => + connection.sendRequest("sandbox.proxySecret.save", params), + /** + * Removes the stored sandbox proxy password from the credential store `sandbox.proxySecret.getLocation` names. Succeeds when no entry exists, so it is safe to call while clearing a proxy password whose origin is unknown. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract. + */ + delete: async (): Promise => + connection.sendRequest("sandbox.proxySecret.delete", {}), + }, + /** @experimental */ + settings: { + /** + * Resolves a stored sandbox settings object into the effective one, filling every value the user left unset with its default. Read this to render or reason about the sandbox configuration a session would actually run under, rather than the sparse document the user wrote. + * + * @param params The stored sandbox settings to resolve against the defaults. + * + * @returns The effective sandbox settings, with every unset value resolved to its default. + */ + applyDefaults: async (params: SandboxApplyDefaultsRequest): Promise => + connection.sendRequest("sandbox.settings.applyDefaults", params), + }, + /** + * Raises a user's sandbox settings to an administrator's managed sandbox floor and returns the result a session should actually run under. The floor can only tighten: an enterprise force-on is reflected even when the user's own `enabled` is false, and a user cannot lower a setting the floor raised. The result is normalized through the declared sandbox shape, so a key reorder or an undeclared passthrough key is not mistaken for a policy change by callers that diff two results. A host must call this rather than merge the two layers itself, because reimplementing the direction of the fold is what turns an administrator's floor into a suggestion. + * + * @param params The user's sandbox settings and the managed sandbox floor to raise them to. + * + * @returns The user's sandbox settings raised to the managed floor. + */ + resolveFlooredConfig: async (params: SandboxResolveFlooredConfigParams): Promise => + connection.sendRequest("sandbox.resolveFlooredConfig", params), + /** + * Builds the effective filesystem policy one `@`-file picker checks its suggestions against, so Git discovery and grant canonicalization run once for the picker's lifetime instead of on every debounced keystroke. Returns no policy when the sandbox is absent or disabled, which the paired filter reads as "keep every candidate". A host must pair this with `sandbox.filterPickerCandidates` rather than resolve a grant list itself, because the picker's grants are narrower than a file tool's — the same path a built-in tool may read can be one the picker must not offer. + * + * @param params The working directory and sandbox settings to build a file-picker policy from. + * + * @returns The prepared file-picker policy, or none when the sandbox is disabled. + */ + preparePickerPolicy: async (params: SandboxPreparePickerPolicyParams): Promise => + connection.sendRequest("sandbox.preparePickerPolicy", params), + /** + * Checks a batch of `@`-file picker suggestions against a policy prepared by `sandbox.preparePickerPolicy` and returns one allow decision per candidate, positionally aligned with the request. Batched deliberately: each check canonicalizes a path component-by-component through the real filesystem, so one call amortizes that over a single hop rather than dozens while the user types. A directory the policy does not itself allow can still be kept as a waypoint to an allowed subtree. An absent policy keeps every candidate. + * + * @param params A batch of file-picker candidates to check against a prepared policy. + * + * @returns Per-candidate allow decisions, positionally aligned with the request. + */ + filterPickerCandidates: async (params: SandboxFilterPickerCandidatesParams): Promise => + connection.sendRequest("sandbox.filterPickerCandidates", params), + /** + * The sandbox a single named feature actually launches under, which is not the same as the session's sandbox. A feature is routed out of the sandbox by its own flag — `sandboxMcpServers`, `sandboxLspServers` — so an enabled session can still start one server confined and another free. Returns a disabled sandbox rather than an error when the host has no sandbox slice at all, so an absent config and an opted-out feature read alike at the launch site. A host must ask per feature rather than reuse the session config, because reading only `enabled` confines a server the user opted out of, or reports as sandboxed one that is not. + * + * @param params A sandbox config and the per-feature routing flag to resolve it for. + * + * @returns The sandbox a named feature actually launches under. + */ + resolveForOptional: async (params: SandboxEffectiveForOptionalParams): Promise => + connection.sendRequest("sandbox.resolveForOptional", params), + /** + * Reports whether this machine can run an OS-level command sandbox at all. A host must ask before it offers sandboxing, because the answer is a property of the machine rather than of any setting: on Linux it depends on bubblewrap being installed, and on Windows on the OS build supporting BaseContainer. Probing it requires spawning a process the first time, so the answer is cached for the life of the runtime. A host that assumes support and enables the sandbox anyway produces a session in which every shell command fails. + * + * @returns True when an OS-level sandbox can run on this host. + */ + checkPlatformSupport: async (): Promise => + connection.sendRequest("sandbox.checkPlatformSupport", {}), + /** + * Reports whether this Windows host's BaseContainer implementation can reach the host's loopback interface directly, which is what a local-network policy needs to avoid routing through the egress proxy. Like `sandbox.checkPlatformSupport`, the answer is a property of the machine's OS build rather than of any setting, and resolving it the first time spawns a platform probe. A host that offers the direct local-network option on a machine that lacks the capability produces a session whose sandboxed commands cannot reach local services. Always false off Windows. + * + * @returns True when the sandbox on this host can reach host loopback directly. + */ + checkWindowsHostLoopbackSupport: async (): Promise => + connection.sendRequest("sandbox.checkWindowsHostLoopbackSupport", {}), + /** + * The explanation a host shows when it refuses an explicit request to turn the sandbox on for a machine that cannot run one. Carries the platform-specific remedy — install bubblewrap, update Windows — and the documentation link, so a host that got `false` from `sandbox.checkPlatformSupport` does not have to compose its own reason and cannot describe the same machine differently from the runtime's own spawn path. + * + * @returns The user-facing refusal, including the platform remedy and the documentation link. + */ + getUnsupportedMessage: async (): Promise => + connection.sendRequest("sandbox.getUnsupportedMessage", {}), + /** + * The notice a host shows when a stored `sandbox` setting is being ignored for this session because the machine cannot enforce it. Distinct from `sandbox.getUnsupportedMessage`, which refuses an explicit request: this one reports that a setting the user already saved is inert, states that commands will run unsandboxed, and confirms the stored setting itself is left alone. A host that stays silent here leaves the user believing a sandbox is active. + * + * @returns The user-facing notice that a saved sandbox setting is inert for this session. + */ + getUnsupportedSettingDisabledNotice: async (): Promise => + connection.sendRequest("sandbox.getUnsupportedSettingDisabledNotice", {}), + /** + * The warning a host prints at startup when a session asked for a sandbox on a machine that cannot run one. The third of the unsupported notices, and the only one that depends on the policy in force: it names the remedy the reader can actually act on, which is the stored setting for an unmanaged host, an administrator for a managed one, and waiting for the policy to resolve when the sandbox stayed on only because the policy could not be determined. Returns no notice when the machine does support a sandbox, so a host can call it unconditionally on the startup path. A host that composes its own warning tells a managed user to change a setting their policy will overwrite. + * + * @param params The managed sandbox policy in force, which selects which remedy the notice offers. + * + * @returns The startup warning, absent when the machine can run a sandbox. + */ + getUnsupportedStartupNotice: async (params: SandboxUnsupportedStartupNoticeParams): Promise => + connection.sendRequest("sandbox.getUnsupportedStartupNotice", params), + /** + * Whether one slash command stays reachable while sandbox enforcement is unavailable and the session is otherwise blocked. The permitted set is deliberately narrow — commands that only report state, and the few that let the user repair or leave the situation — and it is keyed on the arguments as well as the name, so `/sandbox status` is admitted where `/sandbox enable` is not. A host must ask rather than keep its own list: a command this returns false for is one the runtime would refuse anyway, and a divergent list either strands the user with no way out or admits a command that acts on an unenforced session. + * + * @param params One slash command, as typed, to test against the enforcement-block allowlist. + * + * @returns True when the command stays available while enforcement is blocked. + */ + checkEnforcementCommand: async (params: SandboxEnforcementCommandAllowedParams): Promise => + connection.sendRequest("sandbox.checkEnforcementCommand", params), + /** + * Returns the documentation URL a host should point a user at when it declines or downgrades a sandbox setting. The runtime owns the address so every surface cites the same page and a host does not hard-code a link that later moves. + * + * @returns The documentation URL a host cites when it declines or downgrades a sandbox setting. + */ + getDocsUrl: async (): Promise => + connection.sendRequest("sandbox.getDocsUrl", {}), + }, + /** @experimental */ + persistence: { + /** + * Atomically replaces a JSON file's entire contents while holding the runtime's per-path lock. The lock lives in the runtime process, so a host that writes these files itself takes no part in it and can tear a concurrent runtime write; route the write here instead. Unlike a merging write this never reads or parses the existing file, so a previously corrupted snapshot is replaced wholesale rather than failing every later write. Use it only for callers that always persist a complete snapshot. + * + * @param params A complete JSON snapshot to write under the runtime's per-path file lock. + * + * @returns The outcome of a locked snapshot write. + */ + writeRawJsonLocked: async (params: PersistenceWriteRawJsonLockedRequest): Promise => + connection.sendRequest("persistence.writeRawJsonLocked", params), + }, + /** @experimental */ + user: { + /** @experimental */ + settings: { + /** + * Adds or removes one hook key in the user `disabledHooks` setting, rewriting settings.json in place and leaving every other setting untouched. Reports a typed outcome rather than failing the call, so a host can tell an unparseable settings file apart from a write that could not complete. + * + * @param params One hook to disable or re-enable. + * + * @returns Outcome of rewriting the `disabledHooks` setting. + */ + setHookDisabled: async (params: UserSettingsSetHookDisabledRequest): Promise => + connection.sendRequest("user.settings.setHookDisabled", params), + /** + * Reads the user's settings document exactly as `user.settings.load` does, but from a caller-supplied configuration directory instead of the one the server resolved for itself. Use this when a consumer scopes a session to its own Copilot home — the SDK's per-session `configDir` override — so the settings read match the directory that session actually uses. An absent or empty `configDir` resolves the server's own home, making this identical to `user.settings.load`. Hook-validation relaxations are deliberately not caller-supplied: they continue to come from the server's environment, so a consumer cannot widen the host's hook policy through this method. + * + * @param params Selects the configuration directory whose user settings to read. + * + * @returns The stored user settings document, keyed by setting name. + */ + loadForConfigDir: async (params: UserSettingsLoadForConfigDirRequest): Promise => + connection.sendRequest("user.settings.loadForConfigDir", params), + /** + * Reads a repository's checked-in settings document as stored, normalized, together with any diagnostic raised while reading it. This is the repository half of the layering `user.settings.get` reports: `user.settings.load` reads the user's own document, and this reads the one a repository checks in to govern the keys `user.settings.governanceKeys` lists as repo-governable. A document that fails to parse, or that sets a key a repository may not govern, is reported through `warning` while the settings that did load are still returned, so a consumer surfaces the diagnostic and continues rather than failing the session. When `repoRoot` is the user's home directory the Claude-compatible configuration is skipped, so a home-directory session does not inherit a personal config as repository policy. + * + * @param params Selects the repository whose checked-in settings to read. + * + * @returns A repository's settings as stored, together with any diagnostic raised while reading them. + */ + loadRepo: async (params: UserSettingsLoadRepoRequest): Promise => + connection.sendRequest("user.settings.loadRepo", params), + /** + * Reads a repository's settings and merges them over a supplied user layer in one step, returning the settings a consumer should act on inside that repository. Use this rather than reading the two layers separately: the merge is per setting and collection-aware, so a repository adds to the collections it is allowed to influence without replacing the user's own entries, and a whole-document overlay would get that wrong. Any diagnostic raised while reading the repository layer is returned rather than printed, so the consumer decides how to surface it; the settings that did load are merged regardless. + * + * @param params A repository to read settings from, and the user layer they are merged over. + * + * @returns The settings a consumer should act on inside a repository, together with any diagnostic raised while reading the repository layer. + */ + loadAndMergeRepo: async (params: UserSettingsLoadAndMergeRepoRequest): Promise => + connection.sendRequest("user.settings.loadAndMergeRepo", params), + /** + * Reports the on-disk path of the settings document `user.settings.load` and `user.settings.set` read and write. Resolved the same way those methods resolve it, so a host that shows or opens the settings file names the file the runtime actually uses, including the extensionless legacy `settings` file when that is what is present. + * + * @returns The on-disk location of the user's settings document. + */ + getPath: async (): Promise => + connection.sendRequest("user.settings.getPath", {}), + /** + * Reads the user's stored settings together with the diagnostic raised while reading them. This is `user.settings.load` plus the reason part of the document was ignored: that one answers the document alone, so a caller cannot tell a clean read from a salvaged one. Read this when the caller is about to write the file back, where accepting a partial parse would discard every value the loader could not read. The settings that did load are still reported alongside the warning, so a consumer may surface it and continue instead of failing. + * + * @returns The user's stored settings, together with any diagnostic raised while reading them. + */ + loadWithWarning: async (): Promise => + connection.sendRequest("user.settings.loadWithWarning", {}), + /** + * Folds a user settings layer over a managed (enterprise policy) layer and returns the merged settings. The managed layer's governance keys win, `autoTier` resolves through its overridable form, and the two per-entry control maps (`enabledPlugins`, `extraKnownMarketplaces`) union across the layers and are then stamped back from the managed layer, so each entry an administrator declares wins while the user keeps control of the rest. The parameters are named for their layers rather than being a generic base/override pair: nothing about a settings object identifies which layer produced it, so the runtime cannot detect a transposed call, and transposing one resolves the governance keys in the user's favour instead of the administrator's. Supplying only one layer returns it unchanged, and supplying neither returns an empty object. Rejects a managed `autoTier` that is neither a user-configurable tier nor a single-key overridable form, rather than silently dropping it. + * + * @param params The managed (enterprise policy) layer and the user layer to fold together. The field names carry the layer identity: nothing about a settings object identifies which layer produced it, so a generic base/override pair would let a caller transpose them and silently resolve the governance keys in the user's favour. + * + * @returns The user layer folded over the managed layer. + */ + mergeManagedAndUser: async (params: UserSettingsMergeManagedAndUserParams): Promise => + connection.sendRequest("user.settings.mergeManagedAndUser", params), + /** + * Folds a repository's settings layer into the user layer. Each repo-overridable key carries a directional policy chosen so a repository can only add restrictions and never relax the user's own. The two per-entry managed control maps are the exception those directional rules cannot express — a repository may legitimately enable plugins and declare marketplaces — so they are re-stamped afterwards from the runtime's ambient managed policy. That makes the result depend on the runtime process's managed state and not on the arguments alone: a host must call this rather than reimplement the fold, or a repository could relax a restriction the administrator imposed. Rejects when either layer is not a JSON object. + * + * @param params The user layer and the repository layer to fold together. + * + * @returns The repository layer folded into the user layer. + */ + mergeRepoValue: async (params: UserSettingsMergeRepoValueParams): Promise => + connection.sendRequest("user.settings.mergeRepoValue", params), + /** + * Runs the one-time migration that moves a staff account's update channel to stable, and records that it ran. Idempotent and self-gating: it takes a lock over the global-state file, re-reads the completion stamp inside it, and returns `current` rather than migrating twice, so two hosts racing at startup cannot both migrate. Only a `migrated` status means the persisted settings changed and the caller should reload them. An account on the experimental channel is deliberately left alone and reported as `excluded-experimental`. + * + * @param params The filesystem and environment facts a user-settings file is resolved from. Passed explicitly rather than read from the runtime's own process, so a host whose configuration directory differs from its process environment resolves the file it means. + * + * @returns The outcome of the one-time staff update-channel migration. + */ + migrateStaffUpdateChannelOnce: async (params: UserSettingsHostContext): Promise => + connection.sendRequest("user.settings.migrateStaffUpdateChannelOnce", params), + /** + * Runs the one-time reset that clears a staff account's pinned model selection, and reports the timestamp the host should record. Idempotent and self-gating: the reset is gated on a fixed cutoff, so a caller that passes back the timestamp it recorded gets `didReset: false` rather than a second reset. It applies to staff accounts only; any other account is reported as not reset without the settings document being opened. Only `didReset: true` means the persisted settings changed, and only then are the updated settings returned for the host to adopt. + * + * @param params Selects the configuration directory to run the one-time staff model-selection reset against, and supplies the caller's record of whether it has already run. + * + * @returns The outcome of the one-time staff model-selection reset. + */ + resetStaffModelSelectionOnce: async (params: UserSettingsResetStaffModelSelectionOnceRequest): Promise => + connection.sendRequest("user.settings.resetStaffModelSelectionOnce", params), + /** + * Reports the top-level keys in the user settings document that the runtime does not recognize, phrased for display to the user. This is a separate question from the warning `user.settings.loadWithWarning` returns: that one describes parts of the document the loader had to ignore to load the rest, whereas an unknown key parses cleanly and is simply not a setting, so a host that surfaces only the load warning silently accepts a misspelled key that will never take effect. Nothing is reported when the document is absent, unreadable, or failed to load, because an unknown-key list derived from a document the runtime could not read would be misleading rather than helpful. Inspection never modifies the document. + * + * @param params Selects the configuration directory whose user settings document should be inspected for unrecognized top-level keys. + * + * @returns The unrecognized top-level keys found in the user settings document, already phrased for display. + */ + getUnknownKeysWarning: async (params: UserSettingsUnknownKeysWarningRequest): Promise => + connection.sendRequest("user.settings.getUnknownKeysWarning", params), + /** + * Resolves the effective runtime settings for a host that is about to start a session, layering the given overrides over the settings documents, the environment and the platform defaults. A host cannot assemble this itself: the precedence between a user document, a repository document, the managed layers and the environment is the runtime's own rule, and a host that re-implements it drifts from the server it is configuring. The working directory, environment and platform are taken as arguments rather than read from the server process, so a host resolving settings for a directory other than its own gets that directory's answer. Nothing is written; this only computes. + * + * @param params The overrides and host context to resolve effective runtime settings from. + * + * @returns The effective runtime settings, keyed by setting name. Keys this runtime does not recognize are preserved rather than dropped, so a caller reading a document written by a newer runtime keeps every value. + */ + buildRuntime: async (params: UserSettingsBuildRuntimeRequest): Promise => + connection.sendRequest("user.settings.buildRuntime", params), + /** + * Runs the one-time migration that moves a staff account's log level to the current default, and records that it ran. Idempotent and self-gating: it takes a lock over the global-state file and re-reads the completion stamp inside it, so two hosts racing at startup cannot both migrate. Only `refreshed` can have changed the persisted settings, and the settings it returns are the document re-read after the write, so a host should adopt them rather than reloading separately and racing the next writer. + * + * @param params Selects the configuration directory to run the one-time staff log-level migration against, and supplies the caller's record of whether it has already run. + * + * @returns The outcome of the one-time staff log-level migration. + */ + migrateStaffLogLevelOnce: async (params: UserSettingsMigrateStaffLogLevelOnceRequest): Promise => + connection.sendRequest("user.settings.migrateStaffLogLevelOnce", params), + /** + * Runs the one-time cleanup that removes obsolete CLI-owned settings and legacy rubber-duck keys from the user's settings document, and reports what it did. Idempotent and safe to call on every startup: with nothing obsolete left it reports `unchanged` and writes nothing. The cleanup preserves every key it does not recognize, so a setting written by a newer host is never dropped by an older one, and a document that fails to parse is reported `invalid` and left untouched rather than rewritten from a partial read. The cleaned document is not returned: it is written before this replies, so `user.settings.load` reads it authoritatively. A host that surfaces unrecognized keys through `user.settings.unknownKeys` wants this first, because the keys this retires would otherwise be shown to the user as if they were misspelled settings. + * + * @param params Selects the configuration directory whose settings document to clean. + * + * @returns The outcome of the one-time obsolete-settings cleanup. + */ + migrateObsoleteOnDisk: async (params: UserSettingsMigrateObsoleteOnDiskRequest): Promise => + connection.sendRequest("user.settings.migrateObsoleteOnDisk", params), + /** + * Resolves the on-disk locations of the settings files this runtime reads, so a consumer can show or open them. The user path is the same file `user.settings.get`, `user.settings.load`, and `user.settings.set` operate on. Supply `repoRoot` to also resolve that repository's checked-in and local settings paths. A path is reported whether or not the file exists. + * + * @param params Selects which settings file paths to resolve. + * + * @returns On-disk locations of the settings files this runtime reads. A path is reported whether or not the file exists. + */ + getPaths: async (params: UserSettingsGetPathsRequest): Promise => + connection.sendRequest("user.settings.getPaths", params), + /** + * Lists the top-level user-setting keys by who is allowed to govern them: `repo` keys a repository may override through `.github/copilot/settings.json`, `claudeRepo` keys a repository may override through a Claude-compatible config, and `managed` keys an enterprise policy may fix. The lists are static for a given runtime build and fail closed: a key absent from a list cannot be governed that way. + * + * @returns Top-level user-setting keys grouped by who may govern them. + */ + listGovernanceKeys: async (): Promise => + connection.sendRequest("user.settings.listGovernanceKeys", {}), + /** + * Atomically edits one canonical user setting inside its settings-file lock. Reads, canonicalizes touched aliases, applies the edit, validates the resulting top-level setting and writes before releasing the lock. An explicit autoUpdatesChannel set completes the staff migration in the same transaction. Only a malformed existing file returns `invalid`; refused keys, invalid values and I/O errors reject the call. + * + * @param params One atomic edit of a canonical setting in settings.json. + * + * @returns Outcome of an atomic user or repository settings edit. + */ + edit: async (params: UserSettingsEditRequest): Promise => + connection.sendRequest("user.settings.edit", params), + /** + * Rewrites legacy and snake_case setting names in a settings object to their canonical spelling. A caller editing settings.json must normalize before writing: a legacy key left in place survives the write, is re-normalized on the next load, and silently shadows the canonical value the write just stored. + * + * @param params Settings object whose key names should be canonicalized. + * + * @returns Settings object with canonical key names. + */ + normalize: async (params: UserSettingsNormalizeRequest): Promise => + connection.sendRequest("user.settings.normalize", params), + /** + * Diagnoses a set of already-loaded settings layers, reporting each key that will not take effect and why. Settings load tolerantly so that one bad key does not stop a session from starting; this method surfaces what was dropped so a caller can report it instead of silently ignoring the user's intent. Takes the layers as given rather than reading them, so a caller can diagnose exactly the content it is showing, including unsaved edits. Never echoes a setting's value, because a value may be a token or another secret. Marked internal because it exists to carry a CLI call site off the native addon, not to serve a consumer-facing need; it becomes public once an SDK consumer needs to inspect settings health itself. + * + * @param params The settings layers to diagnose. + * + * @returns The settings that will not take effect across the given layers. + */ + diagnoseProblems: async (params: UserSettingsProblemsRequest): Promise => + connection.sendRequest("user.settings.diagnoseProblems", params), + /** + * Validates one top-level setting subtree and writes just that key to settings.json as a single step. Doing both here closes the race a caller cannot close itself: between a separate preflight read and the write, the file may change. Rewriting only the touched key keeps legacy siblings out of settings.json. Answers a status rather than failing, so a validation problem and a write problem stay distinguishable. + * + * @param params A single top-level setting subtree to validate and write. + * + * @returns Outcome of validating and writing one top-level setting subtree. + */ + applyTopKey: async (params: UserSettingsApplyTopKeyRequest): Promise => + connection.sendRequest("user.settings.applyTopKey", params), + /** + * Replaces the user settings file's entire content in one write. A key absent from `settings` is removed, which is what distinguishes this from `user.settings.set`: that one merges, so it can never clear a key the caller has dropped. For a caller saving the result of a raw edit, a merge would silently resurrect every key the user deleted. Records the same one-time mutation marks a per-key write records, so a setting the user has now chosen explicitly stops being migrated for them. + * + * @param params The whole content to store in the user settings file. + */ + writeAll: async (params: UserSettingsWriteAllRequest): Promise => + connection.sendRequest("user.settings.writeAll", params), + /** + * Merges a partial object into one top-level user setting, re-reading the stored value inside this runtime process's settings.json write lock so that concurrent writers in this process to different sub-fields of the same key do not clobber each other. Unlike `user.settings.set`, which replaces a key's whole value with what the caller last read, this keeps sub-fields the caller did not name when both the stored value and supplied value are objects; otherwise the supplied value replaces the stored value. Use it whenever two independent surfaces own different sub-fields of one setting. Returns the key's stored value after the write, in `value`. + * + * @param params Names the user setting to merge into, and the partial object to merge. + * + * @returns The stored value of the user setting after the merge. + */ + update: async (params: UserSettingsUpdateRequest): Promise => + connection.sendRequest("user.settings.update", params), + /** + * Reads settings.json as stored for a caller that is about to edit it. Unlike `user.settings.get`, this returns file values rather than per-key metadata and defaults. Reports `missing` for an absent file and `invalid` when the existing file will not parse. Retired config.json and extensionless files are not read. + * + * @returns Contents of settings.json as stored, for a caller about to rewrite it. + */ + loadForEdit: async (): Promise => + connection.sendRequest("user.settings.loadForEdit", {}), + /** + * Validates one top-level setting subtree without writing it, reporting each problem with the path it applies to. Scoped to a single top-level key on purpose: validating the whole file would reject an edit whenever an unrelated pre-existing field is malformed, locking the user out of the very tool that could fix it. + * + * @param params A single top-level setting subtree to validate. + * + * @returns Outcome of validating one top-level setting subtree. + */ + validate: async (params: UserSettingsValidateRequest): Promise => + connection.sendRequest("user.settings.validate", params), + }, + }, + /** @experimental */ + repo: { + /** @experimental */ + settings: { + /** + * Atomically edits one allowed top-level repository setting. Reads the raw file, preserves unrelated keys, applies one scalar or list edit, validates the result and writes under one file lock, so concurrent sibling or list edits are not lost. A malformed or unsafe existing file returns `invalid` without a write; rejected keys, invalid values and I/O errors reject the call. + * + * @param params One atomic edit of an allowed top-level repository setting. + * + * @returns Outcome of an atomic user or repository settings edit. + */ + edit: async (params: RepoSettingsEditRequest): Promise => + connection.sendRequest("repo.settings.edit", params), + /** + * Reads one repository settings file without settings normalization or layering. The existing JSONC parser accepts comments and trailing commas and applies its prototype-safety rules. Missing or blank files return an empty valid config. Invalid files return an empty config with invalid=true; other read failures reject. + * + * @param params Which repository settings file to read. + * + * @returns Parsed repository settings without normalization or layering. + */ + readRaw: async (params: RepoSettingsReadRawRequest): Promise => + connection.sendRequest("repo.settings.readRaw", params), + /** + * Writes or removes one allowed top-level repository setting through the existing validator and file lock. Preserves unrelated stored keys. Invalid keys, values, existing documents, and I/O errors reject without replacing the file. Deletion requires delete=true; otherwise value must be present. + * + * @param params One key to write or remove in a repository settings file. + */ + writeKey: async (params: RepoSettingsWriteKeyRequest): Promise => + connection.sendRequest("repo.settings.writeKey", params), + /** + * Replaces the repository-overridable settings in one locked write. Allowed keys present in settings are validated and written; allowed keys absent from settings are removed. Other input keys are ignored, and unknown keys already stored in the file remain unchanged. Invalid values or an invalid existing file reject without a partial write. + * + * @param params The complete set of repository-overridable settings to store. + */ + writeAll: async (params: RepoSettingsWriteAllRequest): Promise => + connection.sendRequest("repo.settings.writeAll", params), + }, + }, + /** @experimental */ + featureFlags: { + /** + * Resolves named gates against this installation's cached online-experiment assignment, for the account the runtime is already authenticated as. Unlike `featureFlags.resolve`, which layers static availability with the populations supplied, this reads the experiment assignment the runtime cached for its own current authentication, so a launch can honor a rollout before any session exists. The account is never taken from the request: naming one would read another account's cached assignments. The cached assignment answers immediately and any network refresh happens behind it, so this call does not wait on the network. Not available to an extension, which cannot carry the user's consent to read their assignment. + * + * @param params Caller population and startup inputs to resolve named online-experiment gates for. Carries no account: the runtime resolves the assignment for the authentication it already holds. + * + * @returns The resolved gates, keyed by flag name. + */ + resolveStartupGates: async (params: FeatureFlagsResolveStartupGatesRequest): Promise => + connection.sendRequest("featureFlags.resolveStartupGates", params), + /** + * Lists every feature flag this runtime declares, with the population each one is declared for, its experimental description, and the online-experiment flag it maps to. This is the static catalog: it describes how each flag is defined, not whether it is on for anyone. Use `featureFlags.resolve` for static values with caller overrides, not active runtime values or experiment assignments. + * + * @returns Every feature flag this runtime declares, with the shape each one was declared with. + */ + listMetadata: async (): Promise => + connection.sendRequest("featureFlags.listMetadata", {}), + /** + * Computes static feature flags from the supplied population and overrides without changing runtime state or verifying audience membership. Starts from declared availability, then applies direct feature-flag environment values, user settings, and explicit `flagOverrides`. It does not apply `COPILOT_EXP_*` experiment assignments, authenticated startup gates, or active runtime state. Settings include `enabledFeatureFlags`, the legacy `feature_flags.enabled` list, and supported convenience settings. Supply `baseFlags` to use an existing flag set instead of declared availability. Omit `isStaffGitHub` or `isStaffMicrosoft` when membership was never recorded: a legacy staff account then counts as both populations. + * + * @param params Caller population and overrides for static feature-flag resolution, independent of experiment assignments and active runtime state. + * + * @returns The resolved feature flags, keyed by flag name. + */ + resolve: async (params: FeatureFlagsResolveRequest): Promise => + connection.sendRequest("featureFlags.resolve", params), + }, + /** @experimental */ + permissions: { + /** @experimental */ + locations: { + /** + * Drops stored per-location permission grants whose directory no longer exists, and reports which location keys were removed. Runs against the permission store under the runtime's own Copilot home, the same store `session.permissions.locations.resolve` reads. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract; it is migration plumbing, not a surface consumers are meant to depend on. It can become public once an SDK consumer needs to prune its own permission store. + * + * @returns Which stored location permissions were dropped because their directory is gone. + */ + cleanupOrphaned: async (): Promise => + connection.sendRequest("permissions.locations.cleanupOrphaned", {}), + }, + /** @experimental */ + folderTrust: { + /** + * Reports whether a folder is trusted, reading the persisted trusted-folders list directly rather than through a session. Use this from a host that has no session yet, such as a one-shot command deciding whether to honor a checkout's own settings. It differs from the session-scoped check in two ways that matter: it takes the configuration directory to read the list from, instead of inheriting the session's, and it answers only from the persisted list, so trust a user granted for the lifetime of one session is not visible here. A host that has a session should prefer the session-scoped check, which also covers session-only trust. + * + * @param params A folder to check against a persisted trusted-folders list, and the configuration directory holding that list. + * + * @returns Folder trust check result. + */ + checkTrust: async (params: FolderTrustIsTrustedParams): Promise => + connection.sendRequest("permissions.folderTrust.checkTrust", params), + }, + /** + * Interprets a verbatim managed `permissions` slice into the three policy questions a host asks before granting elevated permissions: whether bypass-permissions mode is forbidden, whether a policy exists that this build could not determine, and whether the layer supplies permission rules the session must enforce. An absent or empty slice means no managed policy applies. The runtime and its permission service share one policy type, so a new managed permission setting is interpreted in exactly one place; a host that reimplements the reading would silently diverge the first time that set grows. Hosts must fail closed on an undetermined policy rather than treating it as permission granted. + * + * @param params The verbatim managed `permissions` slice to interpret. + * + * @returns The policy questions a host asks before granting elevated permissions. + */ + evaluateManagedPolicy: async (params: PermissionsEvaluateManagedPolicyParams): Promise => + connection.sendRequest("permissions.evaluateManagedPolicy", params), + }, + /** @experimental */ + managedSettings: { + /** + * Discovers device-managed settings the way the runtime's own device layer does, and reports the sandbox floor and the load outcome alongside the validated document. This differs from `managedSettings.read` in three ways a policy-enforcing host depends on: a document that validates to an empty object is reported as absent rather than as an empty policy, a discovery error does not suppress a document that still validates, and a failure is reported as a flag rather than only as error text. Hosts that compose the device layer into an effective policy use this, because `managedSettings.read` cannot distinguish "no device policy" from "device policy could not be read" and would silently hand the user's own layer the win. + * + * @returns The device-managed settings layer, with the sandbox floor and the load outcome a host needs to compose it into an effective policy. + */ + readDevice: async (): Promise => + connection.sendRequest("managedSettings.readDevice", {}), + /** + * Retains the supplied managed layers as this runtime process's startup policy and re-applies the result to every live session. Hosts that own their own short-lived process policy use this, so a guarded mutation is checked against the policy the host actually fetched. Retaining is a whole-process replacement rather than a merge, so a host that already resolved a good policy must not retain a partial one over it. The failure flags are honest inputs rather than assumptions: reporting a fetch or load that failed keeps the layers already retained, and the interim fail-closed state, instead of clearing them. + * + * @param params The managed layers to retain as this process's startup policy, with the outcome of fetching each one. + */ + retainPolicy: async (params: ManagedSettingsRetainPolicyParams): Promise => + connection.sendRequest("managedSettings.retainPolicy", params), + /** + * Stamps an effective managed policy's individually-controlled entries onto a user settings document, returning the settings a host should actually act on. The managed layer wins on every plugin spec and marketplace name it names, and leaves everything it does not name as the user wrote it. Hosts use this rather than merging the two documents themselves, because the merge is per entry rather than per document and a whole-document overlay would either discard the user's unmanaged entries or let a user entry outrank a managed one. Omitting the managed layer returns the settings unchanged, which is what an unmanaged host wants. Rejects when either document is present but is not a JSON object rather than treating it as absent, since silently dropping a malformed policy hands the user's layer the win on exactly the entries the policy exists to control. + * + * @param params A user settings document and the effective managed policy whose controlled entries should be stamped onto it. + * + * @returns The user settings with the managed layer's controlled entries stamped on. + */ + applyControls: async (params: ManagedSettingsApplyControlsParams): Promise => + connection.sendRequest("managedSettings.applyControls", params), + /** + * Validates the managed settings a client is supplying for a session it is about to open, and returns them reduced to the keys a client is allowed to set. A client-supplied policy is not the same thing as an administrator's: it may only narrow permissions, so this accepts `permissions` and rejects every other key outright rather than ignoring it. Rejecting is the point — a host that silently dropped an unsupported key would report a policy as applied while running without it. Hosts call this before opening the session rather than letting the open fail, so a malformed policy is reported against the request that carried it. The runtime applies the same rule again when the session starts, so this cannot be used to smuggle a wider document past it. An accepted document that sets nothing comes back as an empty object, which is distinct from supplying nothing at all. + * + * @param params The client-supplied managed settings to validate and normalize. Omitting them is valid and yields no policy. + * + * @returns The accepted managed settings, reduced to the keys a client is allowed to set. + */ + parseClientValue: async (params: ManagedSettingsParseClientValueParams): Promise => + connection.sendRequest("managedSettings.parseClientValue", params), + /** + * Composes the supplied server, device/MDM, device sandbox floor and policy-helper layers into the single effective managed policy a host should enforce, resolving each key across the layers by the runtime's own precedence rules. Hosts that assemble managed layers themselves use this so their precedence matches the runtime's exactly instead of reimplementing it. The composition is not a pure function of its inputs: an advisory sandbox force-on that arrived over the server channel is declined when this host cannot run a sandbox, so the result reflects the runtime host's capabilities, and the returned settings carry `sandboxAdvisoryFloorDeclined: true` when that happened. Enforced channels (device/MDM and policy helper) are never declined, and a server force-on carrying `failIfUnavailable` is preserved so the session fails loudly instead of quietly running unsandboxed. Rejects when a supplied layer is present but is not a JSON object, rather than treating it as unmanaged, because silently dropping a malformed policy would hand the user's own layer the win on exactly the keys the policy exists to control. + * + * @param params The managed-settings layers to compose into an effective policy. Every layer is optional; omitting all of them yields no effective policy. + * + * @returns The effective managed policy composed from the supplied layers. + */ + resolveEffectiveValue: async (params: ManagedSettingsResolveEffectiveValueParams): Promise => + connection.sendRequest("managedSettings.resolveEffectiveValue", params), + /** + * Decides what a host must do to its bypass-permissions state after a managed-policy resolution lands, given the state already in force and the account now signed in. A host holds two pieces of state — which layer disabled bypass mode, and for a server-delivered disable, the account it was pinned to — and cannot decide the next step from the new policy alone. Three rules make that so, and each one is a control rather than a preference. A device/MDM disable outranks the account, so a later server answer never lifts it. An unknown policy, which is what a failed fetch or a refusal this build cannot enforce yields, preserves whatever is in force instead of reverting to permissive. And a server disable is lifted only when the account it was pinned to is no longer the signed-in one, which is what makes signing in to an unrestricted account recover without a restart while a reconnect on the same restricted account does not. Hosts call this rather than reimplementing it because getting the pinning backwards either strands a disable on an account that never carried one or drops one that still applies. `setDisabledState` is omitted when the host must leave its stored flag untouched, which is distinct from being told to set it to false. + * + * @param params The bypass-permissions state a host holds today, the account now signed in, and what the managed-policy resolution said. + * + * @returns The bypass-permissions state a host should hold after the resolution, and what it must do to get there. + */ + resolveBypassDisableTransition: async (params: ManagedSettingsResolveBypassDisableTransitionParams): Promise => + connection.sendRequest("managedSettings.resolveBypassDisableTransition", params), + /** + * Resolves managed (enterprise policy) settings for a host from every layer at once: the account's server-delivered policy, the device/MDM layer, and a platform policy helper. Hosts use this instead of fetching and composing the layers themselves, because the per-layer precedence and the fail-closed rules that apply when a layer cannot be confirmed are the runtime's own and must match the policy a session will later enforce. It does not retain or apply anything to process-global policy state, so a host may call it before any session exists — which is the point, since plugin activation and one-shot commands resolve policy before a session is available to carry it. The returned per-layer outcome flags are load-bearing rather than diagnostic: a host must distinguish an unmanaged device from a managed device whose policy could not be confirmed, and apply its fail-closed gates in the second case. Reports its diagnostics in `logMessages` rather than logging them, so a host that runs before a log sink exists still sees them. + * + * @param params Inputs describing the host and account an ingestion should resolve managed policy for. + * + * @returns The managed policy layers an ingestion resolved, together with the per-layer outcome flags a host needs to decide how much to trust the result. The flags are reported rather than folded into the settings because a host must be able to distinguish an unmanaged device from a managed device whose policy could not be confirmed, and apply its fail-closed gates in the second case. + */ + ingest: async (params: ManagedSettingsIngestParams): Promise => + connection.sendRequest("managedSettings.ingest", params), + /** + * Resolves the managed (enterprise policy) settings a host must enforce from startup, and judges the account against the enterprise sign-in pin in the same pass. This composes the layers the way `managedSettings.ingest` does and then answers the two questions a host cannot answer for itself: which fail-closed floors apply when a layer could not be confirmed, and whether this credential is allowed to sign in at all. The sign-in verdict is the reason a host must not assemble this itself from `managedSettings.ingest` — a pinned organization is an access control, and a host that composed the layers but skipped the pin would let a developer sign in with a personal or another enterprise's account and escape every control that keys off the signed-in identity. The verdict is computed on every resolution, including one that is not retained, so declining to apply policy never widens access. Set `retainAndApply` to keep the resolved policy as this runtime process's startup policy; leave it false to resolve without side effects. + * + * @param params Inputs describing the host and account a startup policy resolution should run for. Mirrors `managedSettings.ingest`, and adds the decision of whether the resolved policy is retained for this runtime process. + * + * @returns The startup policy a resolution composed, the per-layer outcome flags a host needs to decide how much to trust it, and the enterprise sign-in verdict for the credential it ran against. The flags are reported rather than folded into the settings because a host must distinguish an unmanaged device from a managed device whose policy could not be confirmed, and apply its fail-closed gates in the second case. + */ + resolveStartupPolicy: async (params: ManagedSettingsResolveStartupPolicyParams): Promise => + connection.sendRequest("managedSettings.resolveStartupPolicy", params), + /** + * Resolves the managed `remoteControl` policy for an account on its own, for a host deciding whether a remote-control session may start. Composes the same layers as `managedSettings.ingest` and returns only that one key, so a host does not have to know which layer won. An absent policy means the account is unmanaged for this capability and the host may proceed; that default is safe only while absence really does mean "no policy", so a service refusal that says an account policy exists which this client is too old to enforce resolves to an explicit disabled policy instead of to absence. A resolution that cannot be completed is rejected rather than answered as absence, for the same reason. + * + * @param params Inputs describing the host and account whose managed remote-control policy should be resolved. Mirrors `managedSettings.ingest`, and deliberately carries no retention decision: reading this gate never becomes this runtime process's startup policy. + * + * @returns The managed remote-control policy in force for an account, if any. An absent policy means the account is unmanaged for this capability, which a host reads as allowed — so absence here is a positive statement that no policy applies, never a stand-in for a resolution that failed. A resolution that cannot be completed is rejected instead. + */ + loadRemoteControl: async (params: ManagedSettingsLoadRemoteControlParams): Promise => + connection.sendRequest("managedSettings.loadRemoteControl", params), + /** + * Evaluates the device-channel enterprise sign-in pin (MDM plist, registry, or managed-settings file) against a credential, without resolving any server-delivered policy. This is the gate an entry point runs before it branches, and the only one that can refuse a developer's first sign-in: it is local, needs no credential, and a server-delivered pin only ever reaches accounts already inside the organization. It fails closed by construction — a discovery that cannot determine whether a pin exists refuses rather than allows, because a control that fails open under a triggerable condition is not a control. Hosts that also want the server channel should call `managedSettings.resolveStartupPolicy`, which folds both channels into one verdict. + * + * @param params The credential and environment to evaluate the device-channel sign-in pin against. + * + * @returns The verdict of the `forceLoginOrgs` enterprise sign-in pin for one credential. A managed device may pin Copilot to an approved set of GitHub organizations so a developer cannot sign in with a personal — or another enterprise's — account and escape every control that keys off the signed-in identity. A host must refuse to proceed when `allowed` is false; the remaining fields explain the refusal rather than qualifying it. + */ + resolveDeviceForceLoginOrgsGate: async (params: ManagedSettingsResolveDeviceForceLoginOrgsGateParams): Promise => + connection.sendRequest("managedSettings.resolveDeviceForceLoginOrgsGate", params), + }, /** @experimental */ host: { /** @@ -33103,6 +36780,114 @@ export function createInternalServerRpc(connection: MessageConnection) { connect: async (params: ConnectRequest): Promise => connection.sendRequest("connect", params), /** @experimental */ + account: { + /** + * Renders the sign-in banner and the one-line account description the runtime uses for an authentication identity. The runtime words these in several places for its own callers, so a host that announces a sign-in or an account switch should render them from here rather than composing its own, which would let the two spellings drift apart. + * + * @param params The authentication identity whose shared wording should be rendered. + * + * @returns The wording the runtime uses for an authentication identity. + */ + describeAuthInfo: async (params: AccountDescribeAuthInfoRequest): Promise => + connection.sendRequest("account.describeAuthInfo", params), + /** + * Reports whether the supplied identity can create gists. Gists are unavailable on GitHub Enterprise Cloud with data residency, and to Enterprise Managed Users on github.com, so a host that offers gist actions should hide or disable them when this reports no support. + * + * @param params The authentication identity whose gist support should be reported. + * + * @returns Whether the identity can create gists, and why not when it cannot. + */ + getGistSupport: async (params: AccountGetGistSupportRequest): Promise => + connection.sendRequest("account.getGistSupport", params), + /** + * Resolves a path on the identity's GitHub host into an absolute URL, so a host can link to settings or documentation on the enterprise instance the user is signed in to rather than to github.com. Falls back to the public host when no identity is supplied. + * + * @param params A path to resolve against the GitHub host an identity is signed in to. + * + * @returns An absolute URL on the identity's GitHub host. + */ + getWebUrl: async (params: AccountGetWebUrlRequest): Promise => + connection.sendRequest("account.getWebUrl", params), + /** + * Names the environment variable supplying a classic personal access token, if one is set. A classic token cannot be upgraded in place, so a host that finds one should tell the user which variable to unset rather than starting an interactive sign-in that the variable would override. + * + * @returns The environment variable supplying a classic personal access token, when one is set. + */ + getTokenEnvVar: async (): Promise => + connection.sendRequest("account.getTokenEnvVar", {}), + }, + /** @experimental */ + mcp: { + /** @experimental */ + registry: { + /** + * Allocates an ID for one cancellable MCP registry search. The ID exists before the search starts, so callers can cancel before it starts. The networking stack supplies the cancellation namespace. The runtime retains at most 1,024 unused IDs. At capacity, another allocation can reclaim an unused ID. Active searches retain their IDs until they finish. + * + * @returns Request id naming a cancellable registry search. + */ + allocateRequestId: async (): Promise => + connection.sendRequest("mcp.registry.allocateRequestId", {}), + /** + * Searches the MCP registry the supplied credential may read, resolving the registry endpoint from policy first. Hosts usually send credential-free `AuthIdentity`; a `token` identity can be resolved only when it embeds a token, while `env` and `gh-cli` identities can use a token embedded in the request first. The runtime follows registry pages, keeps the newest entry per server name, cuts the list to `limit`, and sorts an empty-query result by GitHub stars. Each server object is carried opaquely. + * + * @param params Registry search terms, the credential to search under, and the request id that makes the search cancellable. + * + * @returns Servers selected from the registry response. + */ + search: async (params: McpRegistrySearchRequest): Promise => + connection.sendRequest("mcp.registry.search", params), + /** + * Abandons the registry search that uses the given request ID. It acts only on IDs from `mcp.registry.allocateRequestId`, so it never cancels another component's request. Answers `canceled: true` when it stops a running search. Answers `canceled: false` for unknown or reclaimed IDs, completed or canceled searches, and unused reservations. It releases an unused reservation, so a later search with that ID is refused. + * + * @param params Registry search to abandon. + * + * @returns Whether the cancel reached a live search. + */ + cancel: async (params: McpRegistryCancelRequest): Promise => + connection.sendRequest("mcp.registry.cancel", params), + }, + /** + * Lists the MCP servers the runtime supplies itself, so a host that disables the built-ins wholesale suppresses exactly the set the runtime would otherwise add. The names are fixed for a build and depend on no configuration, session or account. + * + * @returns Names of the MCP servers the runtime supplies itself. + */ + listBuiltinServerNames: async (): Promise => + connection.sendRequest("mcp.listBuiltinServerNames", {}), + /** + * Reports whether the gh-replaceable GitHub MCP tools may be clipped for a session, which a host feeds back as the `excludeGhReplaceableTools` build option. The tools are redundant only when the session can reach a shell *and* the host has the `gh` that would replace them, so both halves are decided here. The shell half runs the platform shell tool through the runtime's own tool-filter matcher, so entry forms like `builtin:bash` and `builtin:*` behave exactly as they do when a session builds its tool catalog. The host half probes for `gh`, and is skipped entirely when the filters already rule the shell out. Host presence alone is not enough: a session restricted to, say, `view` would otherwise lose the built-in issue-read fallback while having no shell to replace it with. The answer describes the host the runtime runs on, so it is never forwarded to a remote engine. + * + * @param params The session tool filters that decide whether the session still has a shell to run `gh` with. + * + * @returns Whether the gh-replaceable GitHub MCP tools may be clipped for the session described by the request. + */ + shouldExcludeGitHubTools: async (params: McpShouldExcludeGitHubToolsRequest): Promise => + connection.sendRequest("mcp.shouldExcludeGitHubTools", params), + }, + /** @experimental */ + instructions: { + /** + * Returns the prompt the `init` command runs to author repository instructions. A host offering its own initialize action sends this text so the instructions it produces match the ones the CLI produces. The text is fixed for a build and depends on no configuration, session or repository. + * + * @returns The prompt the init command runs. + */ + getInitPrompt: async (): Promise => + connection.sendRequest("instructions.getInitPrompt", {}), + /** + * Discovers the instruction sources for one caller-named location and returns them in the order the runtime applies them. `discover` answers for a set of project paths and derives the rest of its scope itself; this one instead takes every argument from the caller — the extra directories to scan and whether to descend into nested instruction files — and honors them as given. It also includes instruction directories from trusted repository settings, matching native repository discovery. Use it when the caller knows its own scope and no session exists to derive that scope from, such as a host reporting how many instruction files a directory contributes before any session starts. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract; it is migration plumbing, not a surface consumers are meant to depend on. + * + * @param params Explicit discovery arguments for one location, supplied by the caller rather than derived from a session. + * + * @returns Instruction sources discovered across user, repository, and plugin sources. + */ + discoverRepoSources: async (params: InstructionsDiscoverRepoSourcesRequest): Promise => + connection.sendRequest("instructions.discoverRepoSources", params), + /** + * Drops the runtime's in-memory cache of discovered custom instructions so the next discovery reads them from disk again. A host calls this after it changes instruction files, or after it trusts a folder whose instructions an earlier discovery skipped, so that a session started afterwards sees the current files instead of a cached earlier answer. The call performs no I/O of its own and removes nothing from disk; it only invalidates, so the cost lands on the next discovery. Returns once the cache is clear, which is what lets a caller order a session start after it. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract; it is migration plumbing, not a surface consumers are meant to depend on. + */ + clearCache: async (): Promise => + connection.sendRequest("instructions.clearCache", {}), + }, + /** @experimental */ agents: { /** * Lists the agents this runtime ships, by name. A consumer separating shipped agents from ones the user or a plugin authored should compare against these names rather than against `AgentInfo.source`: an authored agent may carry the `builtin` source while not being one of these, and the runtime treats the two as separate questions. `disableableNames` is the subset a user may turn off, which a client needs to decide whether to offer a toggle. `yamlBasedNames` is the subset backed by a shipped YAML definition, which a client needs before asking the runtime to load one. @@ -33167,7 +36952,7 @@ export function createInternalServerRpc(connection: MessageConnection) { loadForConfigDir: async (params: GlobalStateLoadForConfigDirRequest): Promise => connection.sendRequest("globalState.loadForConfigDir", params), /** - * Records one top-level key in the host's machine-wide state, the counterpart to `globalState.load`. A host calls this to remember that it has shown an onboarding step, asked a one-off question, or completed a migration, so the next run can skip it. Only the named key is replaced and the rest of the document is preserved, which lets two writers record different flags without overwriting each other; passing no value removes the key instead. Only the keys a host records itself are writable: `appInstallNudgeResponded`, `appTipShown`, `askedSetupTerminals`, `autoFeedbackLastPromptedAt`, `firstLaunchAt`, `recentModelIds`, `sandboxCredentialProxyCaDeclined` and `sandboxOnboardingShown`. Every other key is refused, including `installedPlugins`, the stored credentials, `trustedFolders`, the staff flags and the signed-in accounts. Plugin enablement must use the plugin APIs, which apply repository and managed-policy checks. + * Records one top-level key in the host's machine-wide state, the counterpart to `globalState.load`. A host calls this to remember that it has shown an onboarding step, asked a one-off question, or completed a migration, so the next run can skip it. Only the named key is replaced and the rest of the document is preserved, which lets two writers record different flags without overwriting each other; passing no value removes the key instead. Only the keys a host records itself are writable: `appTipShown`, `askedSetupTerminals`, `autoFeedbackLastPromptedAt`, `firstLaunchAt`, `recentModelIds`, `sandboxCredentialProxyCaDeclined` and `sandboxOnboardingShown`. Every other key is refused, including `installedPlugins`, the stored credentials, `trustedFolders`, the staff flags and the signed-in accounts. Plugin enablement must use the plugin APIs, which apply repository and managed-policy checks. * * @param params A single top-level key to record in the host's machine-wide state. The write replaces only that key and leaves the rest of the document untouched, so two writers recording different one-off flags do not overwrite each other. The stored credential keys cannot be written through this method. */ @@ -33426,6 +37211,13 @@ export function createSessionRpc(connection: MessageConnection, sessionId: strin */ getEnforcementStatus: async (): Promise => connection.sendRequest("session.sandbox.getEnforcementStatus", { sessionId }), + /** + * Lists possible secret-bearing environment variables available to new sandboxed shells that are not already covered by credential masking. Uses local name rules and known-provider host suggestions, never returns secret values, and changes no settings or network permissions. Returns no suggestions while the session is not sandboxed. + * + * @returns Possible unconfigured secrets in the sandbox shell environment, sorted by variable name, with no secret values. + */ + getCredentialSuggestions: async (): Promise => + connection.sendRequest("session.sandbox.getCredentialSuggestions", { sessionId }), /** * Disables sandboxing for the remainder of the current session and approves the referenced pending sandbox-bypass permission request. The request is rejected unless the exact request is still pending and the effective sandbox policy permits bypass. * @@ -33778,6 +37570,15 @@ export function createSessionRpc(connection: MessageConnection, sessionId: strin */ setReasoningEffort: async (params: ModelSetReasoningEffortRequest): Promise => connection.sendRequest("session.model.setReasoningEffort", { ...params, sessionId }), + /** + * Computes whether the session's current model shows reasoning summaries and how a host should present them: the default Ctrl+T visibility, reasoning headers, the hidden-summaries hint, and whether a persisted `showReasoning` setting is redundant. The runtime folds in the session's model catalog, selection and reasoning-summaries experiment assignment; the host supplies only its own persisted setting and current visibility. This method reads the session's resolved model catalog rather than resolving it, so call `session.model.list` first on a session that has not listed models or run a turn; until then the result is `ready: false`. A session that resolves models without a Copilot catalog, that is a BYOK registry or an alternate provider, never caches one and stays `ready: false`. + * + * @param params Host presentation state the reasoning-summaries policy depends on. + * + * @returns Reasoning-summaries presentation policy for the session's current model. + */ + getReasoningSummaries: async (params: ModelGetReasoningSummariesRequest): Promise => + connection.sendRequest("session.model.getReasoningSummaries", { ...params, sessionId }), /** * 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. * @@ -34822,6 +38623,15 @@ export function createSessionRpc(connection: MessageConnection, sessionId: strin */ reload: async (params?: SessionPluginsReloadRequest): Promise => connection.sendRequest("session.plugins.reload", { ...params, sessionId }), + /** + * Installs or updates the plugins the session's managed settings require now, without waiting for the next automatic attempt after a failure, then reloads plugins. Reports progress and failures with the same `session.info` and `session.warning` events as automatic preparation. Fails when the runtime does not prepare managed plugins for the session. + * + * @param params Plugins required by the session's managed settings to install or update again. + * + * @returns Per-plugin outcomes of retrying the plugins required by the session's managed settings. + */ + retryManaged: async (params?: SessionPluginsRetryManagedRequest): Promise => + connection.sendRequest("session.plugins.retryManaged", { ...params, sessionId }), }, /** @experimental */ provider: { diff --git a/nodejs/src/generated/session-events.ts b/nodejs/src/generated/session-events.ts index 0938ae9744..6e7bb5bd02 100644 --- a/nodejs/src/generated/session-events.ts +++ b/nodejs/src/generated/session-events.ts @@ -217,7 +217,9 @@ export type RemediationAction = /** Review or widen the sandbox policy. The blocked path or host is named by the accompanying message or by the tool result the action arrived with. */ | "review_sandbox_policy" /** Permit outbound network access in the sandbox policy. */ - | "allow_sandbox_outbound"; + | "allow_sandbox_outbound" + /** Discard conversation history back through the blocked request before continuing. */ + | "rewind_conversation"; /** * The session mode the agent is operating in */ @@ -586,6 +588,38 @@ export type ProviderQuotaCapacityState = | "unknown" /** Client-only compatibility placeholder when acquisitionStatus is unavailable or failed; not an observed service verdict. */ | "unavailable"; +/** + * Identity scope of a monthly usage reading. + */ +export type ProviderMonthlyUsageScope = + /** Consumption of the authenticated user within the parent observation's service and returned cycle. */ + | "user" + /** The service did not establish a recognized usage scope. */ + | "unknown"; +/** + * Availability of a service's monthly consumption reading. + */ +export type ProviderMonthlyUsageState = + /** The service reported a usable consumption reading, including an explicit zero. */ + | "available" + /** The service found no usage policy; no consumption amount is known. */ + | "no_policy" + /** The read failed or its data could not be used; consumption is unknown. */ + | "unavailable" + /** The service reported an unrecognized usage state. */ + | "unknown"; +/** + * Units explicitly reported by a quota provider. + */ +export type ProviderQuotaUnit = + /** AI credits, not tokens or currency. */ + | "ai_credits" + /** Requests charged against an entitlement. */ + | "requests" + /** Model tokens. */ + | "tokens" + /** The observation does not establish a quantity unit. */ + | "unknown"; /** * Timing and purpose of a provider quota observation. */ @@ -604,18 +638,6 @@ export type ProviderQuotaQuantityKind = | "advisory_balance" /** No quantity measurement, as with admission-only response headers. */ | "none"; -/** - * Units explicitly reported by a quota provider. - */ -export type ProviderQuotaUnit = - /** AI credits, not tokens or currency. */ - | "ai_credits" - /** Requests charged against an entitlement. */ - | "requests" - /** Model tokens. */ - | "tokens" - /** The observation does not establish a quantity unit. */ - | "unknown"; /** * What initiated a conversation compaction */ @@ -942,6 +964,16 @@ export type AssistantUsageApiEndpoint = | "/responses" /** WebSocket Responses API endpoint. */ | "ws:/responses"; +/** + * Content-Encoding applied to the request body sent on the wire + */ +export type ModelCallRequestBodyEncoding = + /** The request body was sent uncompressed (including every WebSocket request). */ + | "identity" + /** The request body was sent gzip-compressed. */ + | "gzip" + /** The request body was sent zstd-compressed. */ + | "zstd"; /** * Transport used for a successful model call */ @@ -950,6 +982,34 @@ export type AssistantUsageTransport = | "http" /** WebSocket transport. */ | "websocket"; +/** + * Normalized cause of the WebSocket failure that triggered the HTTP fallback + */ +export type ModelCallWebSocketFallbackErrorKind = + /** DNS resolution of the WebSocket host failed. */ + | "dns" + /** The TLS handshake or a TLS record failed. */ + | "tls" + /** A WebSocket connect or read timed out. */ + | "timeout" + /** The peer refused the TCP connection. */ + | "connection_refused" + /** The connection was reset, aborted, or ended mid-stream without a close. */ + | "connection_reset" + /** The peer closed the WebSocket. */ + | "closed_by_peer" + /** The runtime closed the WebSocket itself. */ + | "closed_locally" + /** The socket was already closed before it was used. */ + | "not_connected" + /** The upgrade or request was rejected with an HTTP status. */ + | "http_status" + /** A WebSocket protocol violation or an undecodable frame. */ + | "protocol" + /** The WebSocket request could not be built. */ + | "configuration" + /** A failure that maps onto none of the other kinds. */ + | "other"; /** * Why a WebSocket-capable model call was carried by the HTTP fallback */ @@ -2129,7 +2189,7 @@ export interface ErrorData { */ errorCode?: string; /** - * Category of error (e.g., "authentication", "authorization", "quota", "rate_limit", "context_limit", "query") + * Category of error (e.g., "authentication", "authorization", "policy", "quota", "rate_limit", "context_limit", "query") */ errorType: string; /** @@ -2140,6 +2200,14 @@ export interface ErrorData { * GitHub request tracing ID (x-github-request-id header) for correlating with server-side logs */ providerCallId?: string; + /** + * Sanitized provider-defined error parameter preserved from a structured upstream error envelope, when available. + */ + providerErrorParam?: string; + /** + * Provider-defined error type preserved from a structured upstream error envelope, when available (for example, `invalid_request_error`). + */ + providerErrorType?: string; remediation?: RemediationAction; /** * Copilot service request ID (x-copilot-service-request-id header) for CAPI log correlation @@ -3869,6 +3937,7 @@ export interface ProviderQuotaState { * HTTP status from acquisition, when available. */ httpStatus?: number; + monthlyUsage?: ProviderMonthlyUsage; observationKind?: ProviderQuotaObservationKind; /** * When the runtime observed this reading, not a charge timestamp or guarantee that consumption has settled. @@ -3943,6 +4012,31 @@ export interface ProviderQuotaBudgetMetadata { */ usageAllowedWhenExhausted: boolean; } +/** + * An authoritative monthly usage reading for the observation's provider, account, and service. + */ +/** @experimental */ +export interface ProviderMonthlyUsage { + /** + * Nonnegative finite consumption reported by the service, preserving zero and fractions. Present only when state is available; never computed from tokens or balance differences. + */ + consumedQuantity?: number; + /** + * Start of the service-reported monthly billing cycle, in UTC RFC 3339. + */ + cycleStart?: string; + /** + * Service read time in UTC RFC 3339, not a ledger reconciliation watermark or confirmation of the latest inference charge. + */ + queriedAt?: string; + /** + * End of the service-reported monthly billing cycle, in UTC RFC 3339. + */ + resetOn?: string; + scope: ProviderMonthlyUsageScope; + state: ProviderMonthlyUsageState; + unit: ProviderQuotaUnit; +} /** * Session event "session.context_changed". Updated working directory and git context after the change */ @@ -4305,6 +4399,10 @@ export interface CompactionCompleteCompactionTokensUsed { */ outputTokens?: number; provider?: ModelProviderRef; + /** + * Number of model requests represented by this aggregated compaction usage + */ + requestCount?: number; } /** * Per-request cost and usage data from the CAPI copilot_usage response field @@ -4453,6 +4551,14 @@ export interface PermissionRecoveryData { */ maxAttempts: number; onBlocked: PermissionRecoveryOnBlocked; + /** + * Subagent identifier that owns this recovery episode when the request was delegated through a parent permission service + */ + ownerAgentId?: string; + /** + * Root or child execution-session identifier that owns this recovery episode; omitted for legacy snapshots + */ + ownerSessionId?: string; reason: PermissionRecoveryReason; status: PermissionRecoveryStatus; } @@ -7021,9 +7127,14 @@ export interface AssistantUsageData { */ rejectedPredictionTokens?: number; /** - * Serialized (uncompressed) byte length of the request body. A content-free size signal. + * Serialized (uncompressed) byte length of the request body, before any Content-Encoding. A content-free size signal. */ requestBodyBytes?: number; + requestBodyEncoding?: ModelCallRequestBodyEncoding; + /** + * Byte length of the request body actually sent on the wire for the attempt that produced this outcome, after any Content-Encoding. Equals requestBodyBytes when the body was not compressed. + */ + requestBodyWireBytes?: number; /** * Per-request treatment/eligibility signal returned by the Copilot API in the `X-GitHub-Copilot-Request-TE` response header for the associated model call; `false` when the header was absent or unparseable. */ @@ -7067,7 +7178,24 @@ export interface AssistantUsageData { * Milliseconds spent on the WebSocket attempt before falling back to HTTP */ websocketFallbackAfterMs?: number; + /** + * WebSocket close code received before the HTTP fallback + */ + websocketFallbackCloseCode?: number; + websocketFallbackErrorKind?: ModelCallWebSocketFallbackErrorKind; websocketFallbackReason?: ModelCallWebSocketFallbackReason; + /** + * Whether a WebSocket reconnect was attempted before falling back to HTTP + */ + websocketFallbackReconnectAttempted?: boolean; + /** + * Whether the HTTP fallback started during this model call, rather than being inherited from an earlier call + */ + websocketFallbackStartedThisCall?: boolean; + /** + * HTTP status of the WebSocket upgrade rejection or WebSocket API error that triggered the HTTP fallback + */ + websocketFallbackStatusCode?: number; } /** * Ordered accounting identity assigned under the source session's emission lock. @@ -7322,9 +7450,14 @@ export interface ModelCallFailureData { */ reasoningEffort?: string; /** - * Serialized (uncompressed) byte length of the failed request body. A content-free size signal. + * Serialized (uncompressed) byte length of the failed request body, before any Content-Encoding. A content-free size signal. */ requestBodyBytes?: number; + requestBodyEncoding?: ModelCallRequestBodyEncoding; + /** + * Byte length of the failed request body actually sent on the wire, after any Content-Encoding. Equals requestBodyBytes when the body was not compressed. + */ + requestBodyWireBytes?: number; requestFingerprint?: ModelCallFailureRequestFingerprint; /** * Zero-based orchestrator retry index of the failed attempt @@ -7348,7 +7481,24 @@ export interface ModelCallFailureData { * Milliseconds spent on the WebSocket attempt before falling back to HTTP */ websocketFallbackAfterMs?: number; + /** + * WebSocket close code received before the HTTP fallback + */ + websocketFallbackCloseCode?: number; + websocketFallbackErrorKind?: ModelCallWebSocketFallbackErrorKind; websocketFallbackReason?: ModelCallWebSocketFallbackReason; + /** + * Whether a WebSocket reconnect was attempted before falling back to HTTP + */ + websocketFallbackReconnectAttempted?: boolean; + /** + * Whether the HTTP fallback started during this model call, rather than being inherited from an earlier call + */ + websocketFallbackStartedThisCall?: boolean; + /** + * HTTP status of the WebSocket upgrade rejection or WebSocket API error that triggered the HTTP fallback + */ + websocketFallbackStatusCode?: number; } /** * Content-free structural summary of the failing request for diagnosing malformed 4xx calls @@ -9544,6 +9694,14 @@ export interface PermissionRequestedEvent { */ export interface PermissionRequestedData { agentMode?: SessionMode; + /** + * Delegated agent that owns permission recovery when the owner session multiplexes remote agents. Absent otherwise. + */ + ownerAgentId?: string; + /** + * Execution session that owns permission recovery for this request. Absent on historical events. + */ + ownerSessionId?: string; permissionMode?: PermissionMode; permissionRequest: PermissionRequest; promptRequest?: PermissionPromptRequest; @@ -12795,7 +12953,7 @@ export interface ManagedSettingsResolvedEvent { /** @experimental */ export interface ManagedSettingsResolvedData { /** - * Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers, and forced on when `failClosed` is true. + * Whether an explicit enterprise policy restriction disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers; an unresolved policy does not force this on by itself. */ bypassPermissionsDisabled: boolean; /** @@ -12807,7 +12965,7 @@ export interface ManagedSettingsResolvedData { */ deviceManaged: boolean; /** - * Whether managed policy could not be determined (e.g. a failed server fetch) and the session fell back to the fail-closed restriction. When true, restrictions such as disabling bypass-permissions are enforced even though `settings` may be absent. + * Whether managed policy could not be determined (e.g. a failed server fetch) and unresolved-policy safeguards remain active. This does not by itself disable bypass-permissions; `bypassPermissionsDisabled` reports only an explicit policy restriction. */ failClosed: boolean; /** @@ -12818,6 +12976,7 @@ export interface ManagedSettingsResolvedData { * Whether at least two managed sources supplied permission allowlists, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. */ permissionsAllowIntersected?: boolean; + permissionsContext?: ManagedPermissionsContext; /** * Whether the policy-helper managed-settings layer was present. The policy helper is the weakest channel: it fills keys no enterprise source set and can never replace one. */ @@ -12836,6 +12995,19 @@ export interface ManagedSettingsResolvedData { settings?: JsonValue; source: ManagedSettingsResolvedSource; } +/** + * Reusable managed permission evaluation context. Treat permissions as runtime-owned policy data and retain it verbatim; it includes source-aware composition metadata. This is a snapshot, not a capability or approval token; only use contexts obtained from trusted policy sources. + */ +export interface ManagedPermissionsContext { + /** + * Policy could not be determined; evaluation must deny every operation. + */ + failClosed: boolean; + /** + * Runtime-owned composed permissions object. Absent when no permission policy is configured. + */ + permissions?: JsonValue; +} /** * Session event "session.managed_settings_enforced". Runtime enforcement of enterprise managed settings: fires when the session blocks or caps a runtime action because enterprise policy governs it, so SDK clients can explain *why* an action was governed. Unlike `session.managed_settings_resolved` (which reports *what* is managed), this reports a concrete governed action — e.g. a user or host tried to turn on bypass permissions or Assisted Permissions while the corresponding policy disables it. Emitted live (not persisted to the session event log) on user/host-initiated attempts only, never for silent policy application. Marked experimental while the managed-settings surface stabilizes. */ diff --git a/nodejs/src/index.ts b/nodejs/src/index.ts index ef91cd9689..e95b5362de 100644 --- a/nodejs/src/index.ts +++ b/nodejs/src/index.ts @@ -9,7 +9,12 @@ */ export { CopilotClient } from "./client.js"; -export type { HostLocalServerOptions, HostGitHubEnvironmentOptions } from "./generated/rpc.js"; +export type { + HostLocalServerOptions, + HostGitHubEnvironmentOptions, + ModelGetReasoningSummariesRequest, + ModelGetReasoningSummariesResult, +} from "./generated/rpc.js"; export { AhpHost, type AhpHostExit, @@ -245,6 +250,7 @@ export type { ToolTelemetry, ToolResultObject, ToolSearchConfig, + ImageGenerationConfig, TypedSessionEventHandler, TypedSessionLifecycleHandler, ZodSchema, diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index 5a807c8896..d3722a99d8 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -863,6 +863,21 @@ export function defineTool( return { name, ...config }; } +/** + * Non-persisted opt-in to image generation. + * @experimental This API may change or be removed in a future release. + */ +export interface ImageGenerationConfig { + /** + * Opt in to image generation through an authorized Copilot image model. + * False disables it; omission defaults to disabled on create/cold resume and + * preserves the setting on resident resume. Not persisted: re-supply after + * runtime restart. Policy, permissions, offline mode, and tool filters still + * apply. BYOK image generation is not supported. + */ + enabled?: boolean; +} + /** * SDK-supplied override for the runtime's built-in tool-search behavior. * @@ -2010,6 +2025,8 @@ export type MCPServerConfig = MCPStdioServerConfig | MCPHTTPServerConfig; * Configuration for a custom agent. */ export interface CustomAgentConfig { + /** Declarative hook actions keyed by event name, active only for this agent. */ + hooks?: Record[]>; /** * Unique name of the custom agent. */ @@ -2628,6 +2645,12 @@ export interface SessionConfigBase { */ toolSearch?: ToolSearchConfig; + /** + * Image generation opt-in. Re-supply after runtime restart. + * @experimental This API may change or be removed in a future release. + */ + imageGeneration?: ImageGenerationConfig; + /** * List of tool names to allow. When specified, only these tools will be available. * @@ -2939,7 +2962,11 @@ export interface SessionConfigBase { agent?: string; /** - * Directories to load skills from. + * Explicit discovery-only skill roots. No general filesystem access is granted. + * Available without `enableConfigDiscovery`; skill and managed disables still apply. + * Relative paths resolve once when supplied against the request's effective + * `workingDirectory`, and retained roots do not move when it changes. + * Null and omission are equivalent; `[]` clears only the explicit SDK contribution. */ skillDirectories?: string[]; @@ -2960,6 +2987,27 @@ export interface SessionConfigBase { */ skillProvider?: SkillProvider; + /** + * Explicit discovery-only agent roots, separate from `customAgents` objects. + * No general filesystem access is granted. Available without `enableConfigDiscovery`; + * source and managed disables still apply. + * Relative paths resolve once when supplied against the request's effective + * `workingDirectory`, and retained roots do not move when it changes. + * Null and omission are equivalent; `[]` clears only the explicit SDK contribution. + */ + agentDirectories?: string[]; + + /** + * Explicit discovery-only file-hook roots, separate from callback `hooks`. + * No general filesystem access is granted. Available without `enableConfigDiscovery` + * or `enableFileHooks` (which controls repository-origin hooks only); + * hook and managed disables still apply. + * Relative paths resolve once when supplied against the request's effective + * `workingDirectory`, and retained roots do not move when it changes. + * Null and omission are equivalent; `[]` clears only the explicit SDK contribution. + */ + hookDirectories?: string[]; + /** * Local filesystem paths to Open Plugins-format directories * (https://open-plugins.com/) to load for this session. @@ -2976,7 +3024,12 @@ export interface SessionConfigBase { pluginDirectories?: string[]; /** - * Additional directories to search for custom instruction files. + * Explicit discovery-only instruction roots using the existing SDK instruction formats. + * No general filesystem access is granted. Available without `enableConfigDiscovery`; + * instruction and managed disables still apply. + * Relative paths resolve once when supplied against the request's effective + * `workingDirectory`, and retained roots do not move when it changes. + * Null and omission are equivalent; `[]` clears only the explicit SDK contribution. */ instructionDirectories?: string[]; @@ -3094,9 +3147,9 @@ export interface SessionConfigBase { enableOnDemandInstructionDiscovery?: boolean; /** - * When true, enables loading of file-based hooks from `.github/hooks/`. - * This is separate from the `hooks` callback parameter which gates SDK - * hook event registration. + * When true, enables repository-origin file hooks from `.github/hooks/` and + * trusted repository-configured roots. Does not control explicit `hookDirectories` + * or the `hooks` callback parameter. */ enableFileHooks?: boolean; @@ -3176,6 +3229,15 @@ export interface SessionConfig extends SessionConfigBase { */ sessionId?: string; + /** + * Names the session at creation, as `session.name.set` would, and is validated the + * same way: non-empty, no leading or trailing whitespace, at most 100 UTF-16 units, + * no control characters, and no double quotes. A name also makes the session persist + * from the start, so it can be listed and resumed without ever running a turn. + * Local sessions only: cloud creation rejects it. + */ + name?: string; + /** * Invalidates the process-wide custom-instruction discovery cache before * creating this session. Use when instruction files changed in the same runtime. diff --git a/nodejs/test/account-facts-projection.test.ts b/nodejs/test/account-facts-projection.test.ts new file mode 100644 index 0000000000..5e689bb423 --- /dev/null +++ b/nodejs/test/account-facts-projection.test.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { readFileSync } from "node:fs"; +import { describe, expect, it } from "vitest"; + +describe("account facts Go projection", () => { + const source = readFileSync(new URL("../../go/rpc/zrpc.go", import.meta.url), "utf8"); + + for (const name of ["AccountGetWebURLRequest", "AccountGetWebURLResult"]) { + it(`marks ${name} as internal after Go initialism normalization`, () => { + const comment = source.match(new RegExp(`((?://[^\\n]*\\n)+)type ${name} struct`))?.[1]; + expect(comment).toContain(`// Internal: ${name} `); + }); + } +}); + +describe("account facts Python projection", () => { + const source = readFileSync( + new URL("../../python/copilot/generated/rpc.py", import.meta.url), + "utf8" + ); + const exports = source.match(/^__all__ = \[([\s\S]*?)^\]/m)?.[1]; + + for (const [name, keys] of [ + ["AccountGetWebURLRequest", ["identity", "path"]], + ["AccountGetWebURLResult", ["url"]], + ] as const) { + it(`keeps ${name} private without changing its wire keys`, () => { + const declaration = source.match( + new RegExp(`^class _${name}:\\n[\\s\\S]*?(?=^class )`, "m") + )?.[0]; + expect(declaration).toBeDefined(); + expect(source).not.toMatch(new RegExp(`^class ${name}\\b`, "m")); + expect(exports).toBeDefined(); + expect(exports).not.toContain(`"${name}"`); + expect(exports).not.toContain(`"_${name}"`); + for (const key of keys) { + expect(declaration).toContain(`obj.get("${key}")`); + expect(declaration).toContain(`result["${key}"]`); + } + }); + } +}); diff --git a/nodejs/test/computer-use-oauth-fixture.test.ts b/nodejs/test/computer-use-oauth-fixture.test.ts new file mode 100644 index 0000000000..419563d9dd --- /dev/null +++ b/nodejs/test/computer-use-oauth-fixture.test.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { test } from "vitest"; +import { startComputerUseOAuthServer } from "../../test/harness/test-mcp-computer-use-oauth-server.mjs"; + +test("rejects token exchanges without a registered client", async (context) => { + const { expect } = context; + const starting = startComputerUseOAuthServer(); + context.onTestFinished(async () => { + const [result] = await Promise.allSettled([starting]); + if (result.status === "fulfilled") await result.value.close(); + }); + const server = await starting; + context.signal.throwIfAborted(); + const exchange = (clientId: string) => + fetch(`${server.url}/token`, { + method: "POST", + body: new URLSearchParams({ + grant_type: "authorization_code", + code: "accepted-code", + code_verifier: "test-verifier", + ...(clientId ? { client_id: clientId } : {}), + }), + }); + for (const clientId of ["", "wrong-client", "registered-client"]) { + const response = await exchange(clientId); + expect(response.status).toBe(400); + expect(await response.json()).toEqual({ error: "invalid_client" }); + } + const registration = await fetch(`${server.url}/register`, { method: "POST" }); + expect(registration.status).toBe(201); + const { client_id: clientId } = await registration.json(); + for (const invalidClient of ["", "wrong-client"]) { + const response = await exchange(invalidClient); + expect(response.status).toBe(400); + expect(await response.json()).toEqual({ error: "invalid_client" }); + } + expect((await exchange(clientId)).status).toBe(200); +}); + +test("binds preconfigured-client token exchanges without dynamic registration", async (context) => { + const { expect } = context; + const starting = startComputerUseOAuthServer({ preconfiguredClientId: "app-client" }); + context.onTestFinished(async () => { + const [result] = await Promise.allSettled([starting]); + if (result.status === "fulfilled") await result.value.close(); + }); + const server = await starting; + context.signal.throwIfAborted(); + for (const clientId of ["", "wrong-client", "app-client"]) { + const response = await fetch(`${server.url}/token`, { + method: "POST", + body: new URLSearchParams({ + grant_type: "authorization_code", + code: "accepted-code", + code_verifier: "test-verifier", + ...(clientId ? { client_id: clientId } : {}), + }), + }); + expect(response.status).toBe(clientId === "app-client" ? 200 : 400); + if (clientId !== "app-client") { + expect(await response.json()).toEqual({ error: "invalid_client" }); + } + } + expect(server.requests.map((request) => request.path)).not.toContain("/register"); +}); diff --git a/nodejs/test/csharp-codegen.test.ts b/nodejs/test/csharp-codegen.test.ts index d234e4a107..4d215e1b11 100644 --- a/nodejs/test/csharp-codegen.test.ts +++ b/nodejs/test/csharp-codegen.test.ts @@ -6,6 +6,60 @@ import type { ApiSchema, RpcMethod } from "../../scripts/codegen/utils.ts"; import { postProcessSchema } from "../../scripts/codegen/utils.ts"; import { legacyRequestSchema } from "./legacy-parameters-fixture.ts"; +describe("C# RPC dictionaries", () => { + it.each([undefined, true, { type: "string" } as const])( + "retains open object entries with additionalProperties=%j", + (additionalProperties) => { + const dictionary: JSONSchema7 = { + type: "object", + properties: {}, + ...(additionalProperties === undefined ? {} : { additionalProperties }), + }; + const schema: ApiSchema = { + server: { + sample: { + read: { + rpcMethod: "sample.read", + params: { + title: "SampleRequest", + type: "object", + properties: { environment: dictionary }, + required: ["environment"], + }, + result: { + title: "SampleResult", + type: "object", + properties: { + settings: dictionary, + referenced: { $ref: "#/definitions/Dictionary" }, + entries: { type: "array", items: dictionary }, + closed: { + type: "object", + properties: {}, + additionalProperties: false, + }, + }, + }, + }, + }, + }, + definitions: { Dictionary: dictionary }, + }; + const before = structuredClone(schema); + const code = generateRpcCode(schema); + const valueType = typeof additionalProperties === "object" ? "string" : "JsonElement"; + expect(code).toContain(`IDictionary Environment`); + expect(code).toContain(`IDictionary? Settings`); + expect(code).toContain(`IDictionary? Referenced`); + expect(code).toContain(`IList>? Entries`); + expect(code).toContain("class SampleResultClosed"); + expect(code).not.toContain("class SampleRequestEnvironment"); + expect(code).not.toContain("class SampleResultSettings"); + expect(schema).toEqual(before); + } + ); +}); + function legacySignatureFixture(scope: "server" | "session", legacy: unknown) { const params: JSONSchema7 & { "x-legacy-parameters"?: unknown } = { title: "SampleRequest", @@ -31,6 +85,21 @@ function legacySignatureFixture(scope: "server" | "session", legacy: unknown) { return { schema, params, methods }; } +describe("C# required opaque JSON parameters", () => { + it.each(["server", "session"] as const)("preserves JSON null in %s requests", (scope) => { + const { schema, params } = legacySignatureFixture(scope, undefined); + params.required!.push("config"); + const code = generateRpcCode(schema); + + expect(code).toContain("RunAsync(string name, object? config,"); + expect(code).toContain( + 'Config = CopilotClient.ToJsonElementForWire(config) ?? JsonElement.Parse("null")' + ); + expect(code).not.toContain("ArgumentNullException.ThrowIfNull(config);"); + expect(code).toContain("ArgumentNullException.ThrowIfNull(name);"); + }); +}); + describe("C# additive request entry compatibility", () => { it.each(["server", "session"] as const)( "preserves the exact legacy %s signature and adds a same-name request overload", diff --git a/nodejs/test/customization-directories.test.ts b/nodejs/test/customization-directories.test.ts new file mode 100644 index 0000000000..af06711fa1 --- /dev/null +++ b/nodejs/test/customization-directories.test.ts @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +import { once } from "node:events"; +import { createServer, type Socket } from "node:net"; +import { describe, expect, it, onTestFinished } from "vitest"; +import { + createMessageConnection, + StreamMessageReader, + StreamMessageWriter, + type MessageConnection, +} from "vscode-jsonrpc/node.js"; +import { CopilotClient, RuntimeConnection, approveAll } from "../src/index.js"; +import { getSdkProtocolVersion } from "../src/sdkProtocolVersion.js"; + +describe("customization directory adapters", () => { + it.each([ + { name: "omitted", roots: undefined }, + { name: "empty", roots: [] }, + { name: "relative", roots: ["relative-root", "other-root"] }, + ])("preserves supplied roots on create and resume: $name", async ({ roots }) => { + const requests = new Map>(); + const peers: { connection: MessageConnection; socket: Socket }[] = []; + let client: CopilotClient | undefined; + const server = createServer((socket) => { + const connection = createMessageConnection( + new StreamMessageReader(socket), + new StreamMessageWriter(socket) + ); + peers.push({ connection, socket }); + connection.onRequest("connect", () => ({ + ok: true, + protocolVersion: getSdkProtocolVersion(), + version: "adapter-test", + })); + for (const method of ["session.create", "session.resume"]) { + connection.onRequest(method, (params: Record) => { + requests.set(method, params); + return { sessionId: params.sessionId }; + }); + } + connection.onRequest("session.options.update", () => ({})); + connection.onRequest("session.detach", () => ({ success: true })); + connection.onRequest("session.skills.reload", () => ({})); + connection.listen(); + }); + onTestFinished(async () => { + try { + await client?.forceStop(); + } finally { + for (const peer of peers) { + peer.connection.dispose(); + peer.socket.destroy(); + } + if (server.listening) { + await new Promise((resolve, reject) => + server.close((error) => (error ? reject(error) : resolve())) + ); + } + } + }); + server.listen(0, "127.0.0.1"); + await once(server, "listening"); + const address = server.address(); + if (!address || typeof address === "string") throw new Error("Missing test listener"); + client = new CopilotClient({ + connection: RuntimeConnection.forUri(`127.0.0.1:${address.port}`), + }); + const directories = { + skillDirectories: roots?.map((root) => `${root}-skills`), + agentDirectories: roots?.map((root) => `${root}-agents`), + instructionDirectories: roots?.map((root) => `${root}-instructions`), + hookDirectories: roots?.map((root) => `${root}-hooks`), + }; + const config = { + workingDirectory: "request-cwd", + enableConfigDiscovery: false, + enableFileHooks: false, + ...directories, + onPermissionRequest: approveAll, + }; + const session = await client.createSession(config); + await client.resumeSession(session.sessionId, config); + for (const method of ["session.create", "session.resume"]) { + const request = requests.get(method); + expect(request).toBeDefined(); + for (const [key, expected] of Object.entries(directories)) { + if (expected === undefined) { + expect(request).not.toHaveProperty(key); + } else { + expect(request).toHaveProperty(key, expected); + } + } + expect(request).toHaveProperty("enableConfigDiscovery", false); + expect(request).toHaveProperty("enableFileHooks", false); + expect(request).not.toHaveProperty("additionalDirectories"); + expect(request).not.toHaveProperty("customAgents"); + expect(request).toHaveProperty("hooks", false); + } + }); +}); diff --git a/nodejs/test/e2e/accounts_login.e2e.test.ts b/nodejs/test/e2e/accounts_login.e2e.test.ts index f0b08e7759..2440111bca 100644 --- a/nodejs/test/e2e/accounts_login.e2e.test.ts +++ b/nodejs/test/e2e/accounts_login.e2e.test.ts @@ -43,7 +43,7 @@ function clientForTest(): CopilotClient { return client; } -describe("Shared Entra login", () => { +describe.skipIf(process.platform === "linux")("Shared Entra login", () => { it("returns a cancellable flow before discovery and preserves session ownership", async () => { const client = clientForTest(); await using session = await client.createSession({ onPermissionRequest: approveAll }); diff --git a/nodejs/test/e2e/accounts_platform.e2e.test.ts b/nodejs/test/e2e/accounts_platform.e2e.test.ts new file mode 100644 index 0000000000..ae9bf93726 --- /dev/null +++ b/nodejs/test/e2e/accounts_platform.e2e.test.ts @@ -0,0 +1,180 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { readFile, writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import { describe, expect, it } from "vitest"; +import { approveAll } from "../../src/index.js"; +import { createSdkTestContext } from "./harness/sdkTestContext.js"; + +describe("Account provider platform support", async () => { + const { copilotClient: client } = await createSdkTestContext({ + useStdio: true, + copilotClientOptions: { + env: { + COPILOT_ENTRA_AUTH_AUD: "api://platform-gate-override", + COPILOT_DEBUG_ENTRA_BROKER_URL: "http://127.0.0.1:1/unused-broker", + }, + }, + }); + + it("keeps ordinary providers available and rejects unsupported Entra login", async () => { + const session = await client.createSession({ onPermissionRequest: approveAll }); + expect( + await session.rpc.accounts.enumerate({ query: { kind: "providers" } }) + ).toMatchObject({ + kind: "providers", + items: expect.arrayContaining([ + { kind: "githubDotCom", label: "GitHub.com", available: true }, + { kind: "proxima", label: "GitHub Enterprise Cloud (*.ghe.com)", available: true }, + ]), + }); + const flow = await session.rpc.accounts.login.begin({ kind: "proxima" }); + try { + expect(flow.step.kind).toBe("input-required"); + } finally { + await session.rpc.accounts.login.cancel({ flowId: flow.flowId }); + } + if (process.platform === "linux") { + expect( + await session.rpc.accounts.enumerate({ + query: { kind: "providers", brokerAvailable: true }, + }) + ).toMatchObject({ + kind: "providers", + items: expect.arrayContaining([ + expect.objectContaining({ kind: "entra", available: false }), + ]), + }); + await expect(session.rpc.accounts.login.begin({ kind: "entra" })).rejects.toThrow( + "only on Windows and macOS" + ); + } + }); +}); + +describe("Saved account platform support", async () => { + const { createClient, env, openAiEndpoint } = await createSdkTestContext({ + useStdio: true, + copilotClientOptions: { + gitHubToken: undefined, + useLoggedInUser: true, + env: { + COPILOT_GITHUB_TOKEN: "", + GH_TOKEN: "", + GITHUB_TOKEN: "", + GITHUB_ASKPASS: "", + COPILOT_GH_HOST: "github.com", + GH_HOST: "github.com", + COPILOT_HMAC_KEY: "", + CAPI_HMAC_KEY: "", + GITHUB_COPILOT_API_TOKEN: "", + COPILOT_ENABLE_ALT_PROVIDERS: "false", + COPILOT_DISABLE_KEYTAR: "1", + }, + }, + }); + it.each(["entraEmu", "githubDotCom"] as const)( + "restores %s only where supported", + async (kind) => { + const path = join(env.COPILOT_HOME, "config.json"); + const authTokens = { + "https://github.com:emu:entra": { + token: "ghu_platform_emu", + expiresAt: "2099-01-01T00:00:00Z", + accountType: "entra", + }, + "https://github.com:entra:github": { token: "ghu_platform_github" }, + }; + const login = kind === "entraEmu" ? "emu" : "entra"; + const profile = { + storeTokenPlaintext: true, + loggedInUsers: [ + { + host: "https://github.com", + login: "emu", + kind: "entraEmu", + derivedFrom: "base@tenant", + }, + { host: "https://github.com", login: "entra", kind: "githubDotCom" }, + ], + lastLoggedInUser: { host: "https://github.com", login }, + authTokens, + }; + await writeFile(path, JSON.stringify(profile)); + await openAiEndpoint.setCopilotUserByToken("ghu_platform_emu", { + login: "emu", + copilot_plan: "individual", + }); + await openAiEndpoint.setCopilotUserByToken("ghu_platform_github", { + login: "entra", + copilot_plan: "individual", + }); + const client = createClient({ gitHubToken: undefined, useLoggedInUser: true }); + try { + await client.start(); + expect(await client.getAuthStatus()).toMatchObject({ + isAuthenticated: kind === "githubDotCom" || process.platform !== "linux", + }); + } finally { + await client.stop(); + } + // The runtime adds a comment header to this JSON fixture. + const saved = (await readFile(path, "utf8")).replace(/^\/\/.*$/gm, ""); + expect(JSON.parse(saved).authTokens).toEqual(authTokens); + } + ); + + it("checks platform support before setCredentials replaces the selected account", async () => { + const path = join(env.COPILOT_HOME, "config.json"); + const authTokens = { + "https://github.com:emu:entra": { token: "ghu_platform_emu" }, + "https://github.com:ordinary:github": { token: "ghu_platform_github" }, + }; + await writeFile( + path, + JSON.stringify({ + storeTokenPlaintext: true, + loggedInUsers: [ + { host: "https://github.com", login: "emu" }, + { host: "https://github.com", login: "ordinary", kind: "githubDotCom" }, + ], + lastLoggedInUser: { host: "https://github.com", login: "ordinary" }, + authTokens, + }) + ); + await openAiEndpoint.setCopilotUserByToken("ghu_platform_github", { + login: "ordinary", + copilot_plan: "individual", + }); + const client = createClient({ gitHubToken: undefined, useLoggedInUser: true }); + try { + const session = await client.createSession({ onPermissionRequest: approveAll }); + const original = await session.rpc.gitHubAuth.getStatus(); + expect(original).toMatchObject({ isAuthenticated: true, login: "ordinary" }); + const update = session.rpc.gitHubAuth.setCredentials({ + credentials: { + type: "user", + host: "https://github.com", + login: "emu", + copilotUser: { login: "emu" }, + }, + }); + if (process.platform === "linux") { + await expect(update).rejects.toThrow("only on Windows and macOS"); + expect(await session.rpc.gitHubAuth.getStatus()).toEqual(original); + } else { + expect(await update).toMatchObject({ success: true }); + expect(await session.rpc.gitHubAuth.getStatus()).toMatchObject({ + isAuthenticated: true, + login: "emu", + }); + } + } finally { + await client.stop(); + } + const saved = (await readFile(path, "utf8")).replace(/^\/\/.*$/gm, ""); + expect(JSON.parse(saved).authTokens).toEqual(authTokens); + }); +}); diff --git a/nodejs/test/e2e/agent_scoped_hooks.e2e.test.ts b/nodejs/test/e2e/agent_scoped_hooks.e2e.test.ts new file mode 100644 index 0000000000..56664421bb --- /dev/null +++ b/nodejs/test/e2e/agent_scoped_hooks.e2e.test.ts @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { readFile, writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import { describe, expect, it } from "vitest"; +import { approveAll } from "../../src/index.js"; +import { createSdkTestContext } from "./harness/sdkTestContext.js"; + +describe("Agent-scoped hooks", async () => { + const { copilotClient: client, workDir } = await createSdkTestContext(); + + it("runs scoped commands additively and follows selection without model requests", async () => { + const script = join(workDir, "scoped-hook.cjs"); + const log = join(workDir, "scoped-hook.log"); + await writeFile( + script, + `const fs = require("node:fs"); +const [log, label] = process.argv.slice(2); +fs.appendFileSync(log, label + "\\n"); +process.stdout.write(JSON.stringify({ handled: true, responseContent: label })); +` + ); + let sessionHookCalls = 0; + const scoped = (name: string) => ({ + name, + prompt: "No model request is necessary for this test.", + hooks: { + userPromptSubmitted: [ + { type: "command", exec: process.execPath, args: [script, log, name] }, + ], + }, + }); + const session = await client.createSession({ + onPermissionRequest: approveAll, + customAgents: [scoped("FIRST_SCOPE"), scoped("SECOND_SCOPE")], + hooks: { + onUserPromptSubmitted: () => { + sessionHookCalls++; + return { + additionalContext: "Session hook ran.", + handled: true, + responseContent: "SESSION_ONLY", + }; + }, + }, + }); + try { + const selected = await session.rpc.agent.select({ name: "FIRST_SCOPE" }); + expect(selected.agent.hooks).toBeDefined(); + expect((await session.sendAndWait({ prompt: "First request" }))?.data.content).toBe( + "FIRST_SCOPE" + ); + await session.rpc.agent.select({ name: "SECOND_SCOPE" }); + expect((await session.sendAndWait({ prompt: "Second request" }))?.data.content).toBe( + "SECOND_SCOPE" + ); + await session.rpc.agent.deselect(); + expect((await session.sendAndWait({ prompt: "Default request" }))?.data.content).toBe( + "SESSION_ONLY" + ); + expect(sessionHookCalls).toBe(3); + expect(await readFile(log, "utf8")).toBe("FIRST_SCOPE\nSECOND_SCOPE\n"); + } finally { + await session.disconnect(); + } + }); +}); diff --git a/nodejs/test/e2e/assisted_autopilot_permission.e2e.test.ts b/nodejs/test/e2e/assisted_autopilot_permission.e2e.test.ts index 9349d0091d..37d6346089 100644 --- a/nodejs/test/e2e/assisted_autopilot_permission.e2e.test.ts +++ b/nodejs/test/e2e/assisted_autopilot_permission.e2e.test.ts @@ -4,15 +4,16 @@ import { execFileSync } from "node:child_process"; import { existsSync } from "node:fs"; -import { mkdir, rm, writeFile } from "node:fs/promises"; +import { mkdir, realpath, rm, writeFile } from "node:fs/promises"; import { createServer } from "node:http"; import { join } from "node:path"; import { text } from "node:stream/consumers"; -import { describe, expect, it, onTestFinished } from "vitest"; +import { describe, expect, it, onTestFailed, onTestFinished } from "vitest"; import { createAttributedPermissionResult, type CopilotSession, type NamedProviderConfig, + type PermissionRequest, type ProviderModelConfig, } from "../../src/index.js"; import { createSdkTestContext } from "./harness/sdkTestContext.js"; @@ -21,14 +22,61 @@ import { getNextEventOfType } from "./harness/sdkTestHelper.js"; const JUDGE_OUTPUT = "ALLOW: Explicitly authorized bounded filename search."; const HUMAN_REVIEW_OUTPUT = "DENY: Ask the registered human permission handler."; const SHELL_TOOL = process.platform === "win32" ? "powershell" : "bash"; +const EARLIER_RESTRICTION_MARKER = "ASSISTED_AUTHORIZATION_RESTRICT"; +const LATER_AUTHORIZATION_MARKER = "ASSISTED_AUTHORIZATION_ALLOW"; +const AUTHORIZATION_FIXTURE = "assisted-authorization-updated"; + +type ContractRecommendation = "approve" | "requireApproval" | "excluded"; +type ContractScope = "root" | "subagent"; + +function contractMarker(recommendation: ContractRecommendation): string { + return recommendation === "requireApproval" ? "REQUIRE" : recommendation.toUpperCase(); +} + +function contractShellCallId(scope: ContractScope, recommendation: ContractRecommendation): string { + const owner = scope === "subagent" ? "child" : "root"; + return `assisted-contract-${owner}-${recommendation}`; +} + +function contractFixtureName(scope: ContractScope, recommendation: ContractRecommendation): string { + return `assisted-contract-${recommendation}-${scope}`; +} describe("Assisted permission handling in Autopilot", async () => { const { copilotClient: client, workDir } = await createSdkTestContext({ useStdio: true }); - it("executes eligible shell and path ALLOWs without calling created or resumed SDK hosts", async () => { + const scenarioGroups = ["routing", "updated authorization", "recovery"] as const; + it.each(scenarioGroups)("Assisted Autopilot permissions (%s)", async (group) => { let agentCalls = 0; const judgeOutputs: string[] = []; + const authorizationJudgeRequests: string[] = []; const providerFailures: Error[] = []; + const progress = { + scenario: "fixture", + phase: "setup", + permissionCallbacks: 0, + permissionRequests: 0, + toolCompletions: 0, + taskCompletions: 0, + lastEventType: "none", + }; + const recordEvent = (type: string) => { + progress.lastEventType = type; + if (type === "permission.requested") progress.permissionRequests++; + if (type === "tool.execution_complete") progress.toolCompletions++; + if (type === "session.task_complete") progress.taskCompletions++; + }; + onTestFailed(() => { + console.error( + `[Assisted fixture progress] ${JSON.stringify({ + ...progress, + agentCalls, + judgeCalls: judgeOutputs.length, + authorizationJudgeCalls: authorizationJudgeRequests.length, + providerFailures: providerFailures.length, + })}` + ); + }); const outsideDir = `${workDir}-outside`; await mkdir(outsideDir, { recursive: true }); await writeFile(join(outsideDir, "approval-probe.txt"), "ASSISTED_READ_PROBE\n"); @@ -42,6 +90,10 @@ describe("Assisted permission handling in Autopilot", async () => { }; const isJudge = body.model === "gpt-6-luna"; const messages = JSON.stringify(body.messages); + const latestUserMessage = [...body.messages] + .reverse() + .find((entry) => entry.role === "user"); + const latestUserText = JSON.stringify(latestUserMessage?.content); let message: | { role: "assistant"; content: string } | { @@ -54,9 +106,27 @@ describe("Assisted permission handling in Autopilot", async () => { }>; }; if (isJudge) { - const output = messages.includes("assisted-human-ask") - ? HUMAN_REVIEW_OUTPUT - : JUDGE_OUTPUT; + const earlierRestrictionIndex = messages.lastIndexOf( + EARLIER_RESTRICTION_MARKER + ); + const laterAuthorizationIndex = messages.lastIndexOf( + LATER_AUTHORIZATION_MARKER + ); + const authorizationRoute = laterAuthorizationIndex !== -1; + if (authorizationRoute) { + authorizationJudgeRequests.push(messages); + } + const contractRequiresApproval = + messages.includes(contractFixtureName("root", "requireApproval")) || + messages.includes(contractFixtureName("subagent", "requireApproval")); + const output = authorizationRoute + ? earlierRestrictionIndex !== -1 && + earlierRestrictionIndex < laterAuthorizationIndex + ? JUDGE_OUTPUT + : HUMAN_REVIEW_OUTPUT + : messages.includes("assisted-human-ask") || contractRequiresApproval + ? HUMAN_REVIEW_OUTPUT + : JUDGE_OUTPUT; judgeOutputs.push(output); message = { role: "assistant", content: output }; } else { @@ -64,90 +134,259 @@ describe("Assisted permission handling in Autopilot", async () => { const toolResultCount = body.messages.filter( (entry) => entry.role === "tool" ).length; - const primeIndex = messages.lastIndexOf("ASSISTED_SESSION_PRIME"); - const shellIndex = messages.lastIndexOf("ASSISTED_SHELL_"); - const pathIndex = messages.lastIndexOf("ASSISTED_PATH_ROUTE"); - const humanIndex = messages.lastIndexOf("ASSISTED_HUMAN_"); - const primeRoute = primeIndex > Math.max(shellIndex, pathIndex, humanIndex); - const humanRoute = humanIndex > Math.max(primeIndex, shellIndex, pathIndex); - const shellRoute = shellIndex > Math.max(primeIndex, pathIndex, humanIndex); - const resumedShellRoute = - messages.lastIndexOf("ASSISTED_SHELL_RESUME_ROUTE") === shellIndex; - const humanApproved = messages.includes("ASSISTED_HUMAN_APPROVE_"); - const humanLifecycle = - messages.includes("ASSISTED_HUMAN_APPROVE_RESUME_ROUTE") || - messages.includes("ASSISTED_HUMAN_DENY_RESUME_ROUTE") - ? "resume" - : "create"; - const finalText = humanRoute - ? humanApproved - ? "human-approved" - : "human-denied" - : shellRoute - ? "shell-approved" - : "approval-probe.txt"; - message = primeRoute - ? { role: "assistant", content: "prime-ready" } - : toolResultCount >= 2 - ? { + const contractChildRoute = latestUserText.includes("ASSISTED_CONTRACT_CHILD_"); + const contractSubagentRoute = latestUserText.includes( + "ASSISTED_CONTRACT_SUBAGENT_" + ); + const contractRootRoute = latestUserText.includes("ASSISTED_CONTRACT_ROOT_"); + const authorizationRestrictionRoute = latestUserText.includes( + EARLIER_RESTRICTION_MARKER + ); + const authorizationAllowRoute = latestUserText.includes( + LATER_AUTHORIZATION_MARKER + ); + if (authorizationRestrictionRoute) { + message = { role: "assistant", content: "restriction-recorded" }; + } else if (authorizationAllowRoute) { + message = + toolResultCount >= 2 + ? { role: "assistant", content: "authorization-updated" } + : toolResultCount === 1 + ? { + role: "assistant", + content: "", + tool_calls: [ + { + id: "assisted-authorization-task-complete", + type: "function", + function: { + name: "task_complete", + arguments: JSON.stringify({ + summary: "authorization-updated", + }), + }, + }, + ], + } + : { + role: "assistant", + content: "", + tool_calls: [ + { + id: "assisted-authorization-shell", + type: "function", + function: { + name: SHELL_TOOL, + arguments: JSON.stringify({ + command: + process.platform === "win32" + ? `New-Item -ItemType Directory -Path ${AUTHORIZATION_FIXTURE}` + : `mkdir ${AUTHORIZATION_FIXTURE}`, + description: + "Create the fixture authorized by the latest instruction", + }), + }, + }, + ], + }; + } else if (contractChildRoute || contractSubagentRoute || contractRootRoute) { + const recommendation: ContractRecommendation = latestUserText.includes( + "_REQUIRE" + ) + ? "requireApproval" + : latestUserText.includes("_EXCLUDED") + ? "excluded" + : "approve"; + const scope: ContractScope = + contractChildRoute || contractSubagentRoute ? "subagent" : "root"; + const finalText = `contract-${scope}-${ + recommendation === "approve" ? "approved" : "blocked" + }`; + if (contractSubagentRoute) { + message = + toolResultCount === 0 + ? { + role: "assistant", + content: "", + tool_calls: [ + { + id: `assisted-contract-task-${recommendation}`, + type: "function", + function: { + name: "task", + arguments: JSON.stringify({ + name: "assisted-contract", + description: + "Exercise an Assisted permission request", + prompt: `ASSISTED_CONTRACT_CHILD_${contractMarker( + recommendation + )}: Run the requested shell command once and report whether it completed.`, + agent_type: "task", + mode: "sync", + }), + }, + }, + ], + } + : { + role: "assistant", + content: "", + tool_calls: [ + { + id: `assisted-contract-task-complete-${recommendation}`, + type: "function", + function: { + name: "task_complete", + arguments: JSON.stringify({ + summary: finalText, + }), + }, + }, + ], + }; + } else if (contractChildRoute && toolResultCount > 0) { + message = { role: "assistant", content: finalText }; + } else if ( + (contractChildRoute || contractRootRoute) && + (toolResultCount === 0 || + (recommendation === "excluded" && toolResultCount === 1)) + ) { + const command = + recommendation === "excluded" + ? process.platform === "win32" + ? "Get-Content approval-probe.txt | & $runner" + : "printf payload | $runner" + : process.platform === "win32" + ? `New-Item -ItemType Directory -Path ${contractFixtureName( + scope, + recommendation + )}` + : `mkdir ${contractFixtureName(scope, recommendation)}`; + message = { role: "assistant", - content: finalText, - } - : toolResultCount === 1 - ? { - role: "assistant", - content: "", - tool_calls: [ - { - id: "assisted-autopilot-task-complete", - type: "function", - function: { - name: "task_complete", - arguments: JSON.stringify({ - summary: finalText, - }), + content: "", + tool_calls: [ + { + id: + toolResultCount === 0 + ? contractShellCallId(scope, recommendation) + : `${contractShellCallId( + scope, + recommendation + )}-retry`, + type: "function", + function: { + name: SHELL_TOOL, + arguments: JSON.stringify({ + command, + description: "Exercise Assisted permission routing", + }), + }, + }, + ], + }; + } else { + message = { + role: "assistant", + content: "", + tool_calls: [ + { + id: `assisted-contract-task-complete-${recommendation}`, + type: "function", + function: { + name: "task_complete", + arguments: JSON.stringify({ summary: finalText }), + }, + }, + ], + }; + } + } else { + const primeIndex = messages.lastIndexOf("ASSISTED_SESSION_PRIME"); + const shellIndex = messages.lastIndexOf("ASSISTED_SHELL_"); + const pathIndex = messages.lastIndexOf("ASSISTED_PATH_ROUTE"); + const humanIndex = messages.lastIndexOf("ASSISTED_HUMAN_"); + const primeRoute = primeIndex > Math.max(shellIndex, pathIndex, humanIndex); + const humanRoute = humanIndex > Math.max(primeIndex, shellIndex, pathIndex); + const shellRoute = shellIndex > Math.max(primeIndex, pathIndex, humanIndex); + const resumedShellRoute = + messages.lastIndexOf("ASSISTED_SHELL_RESUME_ROUTE") === shellIndex; + const humanApproved = messages.includes("ASSISTED_HUMAN_APPROVE_"); + const humanLifecycle = + messages.includes("ASSISTED_HUMAN_APPROVE_RESUME_ROUTE") || + messages.includes("ASSISTED_HUMAN_DENY_RESUME_ROUTE") + ? "resume" + : "create"; + const finalText = humanRoute + ? humanApproved + ? "human-approved" + : "human-denied" + : shellRoute + ? "shell-approved" + : "approval-probe.txt"; + message = primeRoute + ? { role: "assistant", content: "prime-ready" } + : toolResultCount >= 2 + ? { + role: "assistant", + content: finalText, + } + : toolResultCount === 1 + ? { + role: "assistant", + content: "", + tool_calls: [ + { + id: "assisted-autopilot-task-complete", + type: "function", + function: { + name: "task_complete", + arguments: JSON.stringify({ + summary: finalText, + }), + }, + }, + ], + } + : { + role: "assistant", + content: "", + tool_calls: [ + { + id: shellRoute + ? "assisted-autopilot-shell" + : humanRoute + ? "assisted-autopilot-human" + : "assisted-autopilot-glob", + type: "function", + function: + shellRoute || humanRoute + ? { + name: SHELL_TOOL, + arguments: JSON.stringify({ + command: humanRoute + ? process.platform === "win32" + ? `New-Item -ItemType Directory -Path assisted-human-ask-${humanApproved ? "approve" : "deny"}-${humanLifecycle}` + : `mkdir assisted-human-ask-${humanApproved ? "approve" : "deny"}-${humanLifecycle}` + : process.platform === "win32" + ? `New-Item -ItemType Directory -Path assisted-shell-${resumedShellRoute ? "resume" : "create"}` + : `mkdir assisted-shell-${resumedShellRoute ? "resume" : "create"}`, + description: humanRoute + ? "Create the human-reviewed SDK permission fixture" + : "Create the authorized SDK permission fixture", + }), + } + : { + name: "glob", + arguments: JSON.stringify({ + pattern: "*.txt", + path: outsideDir, + }), + }, }, - }, - ], - } - : { - role: "assistant", - content: "", - tool_calls: [ - { - id: shellRoute - ? "assisted-autopilot-shell" - : humanRoute - ? "assisted-autopilot-human" - : "assisted-autopilot-glob", - type: "function", - function: - shellRoute || humanRoute - ? { - name: SHELL_TOOL, - arguments: JSON.stringify({ - command: humanRoute - ? process.platform === "win32" - ? `New-Item -ItemType Directory -Path assisted-human-ask-${humanApproved ? "approve" : "deny"}-${humanLifecycle}` - : `mkdir assisted-human-ask-${humanApproved ? "approve" : "deny"}-${humanLifecycle}` - : process.platform === "win32" - ? `New-Item -ItemType Directory -Path assisted-shell-${resumedShellRoute ? "resume" : "create"}` - : `mkdir assisted-shell-${resumedShellRoute ? "resume" : "create"}`, - description: humanRoute - ? "Create the human-reviewed SDK permission fixture" - : "Create the authorized SDK permission fixture", - }), - } - : { - name: "glob", - arguments: JSON.stringify({ - pattern: "*.txt", - path: outsideDir, - }), - }, - }, - ], - }; + ], + }; + } } const choice = { @@ -231,23 +470,40 @@ describe("Assisted permission handling in Autopilot", async () => { modelId: "gpt-4o", wireModel: id, })); - const scenarios = [ - { route: "shell", lifecycle: "create", decision: "judge" }, - { route: "path", lifecycle: "create", decision: "judge" }, - { route: "shell", lifecycle: "resume", decision: "judge" }, - { route: "path", lifecycle: "resume", decision: "judge" }, - { route: "human", lifecycle: "create", decision: "approve" }, - { route: "human", lifecycle: "create", decision: "deny" }, - { route: "human", lifecycle: "resume", decision: "approve" }, - { route: "human", lifecycle: "resume", decision: "deny" }, - ] as const; + const scenarios = + group === "routing" + ? ([ + { route: "shell", lifecycle: "create", decision: "judge" }, + { route: "path", lifecycle: "create", decision: "judge" }, + { route: "shell", lifecycle: "resume", decision: "judge" }, + { route: "path", lifecycle: "resume", decision: "judge" }, + { route: "human", lifecycle: "create", decision: "approve" }, + { route: "human", lifecycle: "create", decision: "deny" }, + { route: "human", lifecycle: "resume", decision: "approve" }, + { route: "human", lifecycle: "resume", decision: "deny" }, + ] as const) + : []; for (const scenario of scenarios) { + progress.scenario = `routing:${JSON.stringify(scenario)}`; + progress.phase = "create-session"; let permissionCallbacks = 0; + const expectedRecommendation = + scenario.decision === "judge" ? "approve" : "requireApproval"; + const recommendationByToolCallId = new Map(); + const recommendationWaiters = new Map< + string, + (recommendation: string | undefined) => void + >(); const recommendations: string[] = []; const recoveryStatuses: string[] = []; const toolResults: Array<{ success?: boolean; result?: { content?: string } }> = []; const decisionSources: string[] = []; - const taskOutcomes: Array<{ success?: boolean; summary?: string }> = []; + const taskOutcomes: Array<{ + success?: boolean; + summary?: string; + outcome?: string; + reason?: string; + }> = []; const sessionConfig = { model: "local/gpt-5.6-sol", providers, @@ -259,8 +515,27 @@ describe("Assisted permission handling in Autopilot", async () => { AUTO_APPROVAL: true, ASSISTED_PERMISSIONS_V2: true, }, - onPermissionRequest: () => { + onPermissionRequest: async (request: PermissionRequest) => { + progress.permissionCallbacks++; permissionCallbacks++; + if (!request.toolCallId) { + throw new Error( + "Expected the permission request to identify its tool call" + ); + } + const recommendation = recommendationByToolCallId.has(request.toolCallId) + ? recommendationByToolCallId.get(request.toolCallId) + : await new Promise((resolve) => { + recommendationWaiters.set(request.toolCallId!, resolve); + }); + recommendationByToolCallId.delete(request.toolCallId); + if (recommendation !== expectedRecommendation) { + throw new Error( + `Expected Assisted recommendation ${expectedRecommendation}, got ${String( + recommendation + )}` + ); + } if (scenario.decision === "judge") { return createAttributedPermissionResult( { kind: "approve-once" }, @@ -288,15 +563,27 @@ describe("Assisted permission handling in Autopilot", async () => { session = await client.createSession(sessionConfig); if (scenario.lifecycle === "resume") { const sessionId = session.sessionId; + progress.phase = "prime-turn"; await session.sendAndWait({ prompt: "ASSISTED_SESSION_PRIME: Reply with prime-ready without tools.", }); + progress.phase = "configure-before-resume"; + await configureAssistedAutopilot(session, workDir); + progress.phase = "verify-before-resume"; + await expectAssistedAutopilotConfigured(session, workDir); + progress.phase = "disconnect-before-resume"; await session.disconnect(); session = undefined; + progress.phase = "resume-session"; session = await client.resumeSession(sessionId, sessionConfig); + progress.phase = "verify-after-resume"; + await expectAssistedAutopilotConfigured(session, workDir); + } else { + progress.phase = "configure-session"; + await configureAssistedAutopilot(session, workDir); } - await configureAssistedAutopilot(session, workDir); session.on((event) => { + recordEvent(event.type); if (event.type === "permission.requested") { const promptRequest = ( event.data as { @@ -309,6 +596,20 @@ describe("Assisted permission handling in Autopilot", async () => { const recommendation = promptRequest?.assistedApproval?.recommendation ?? promptRequest?.autoApproval?.recommendation; + const toolCallId = ( + event.data as { + permissionRequest?: { toolCallId?: string }; + } + ).permissionRequest?.toolCallId; + if (toolCallId) { + const waiter = recommendationWaiters.get(toolCallId); + if (waiter) { + recommendationWaiters.delete(toolCallId); + waiter(recommendation); + } else { + recommendationByToolCallId.set(toolCallId, recommendation); + } + } if (recommendation) recommendations.push(recommendation); } else if (event.type === "permission.completed") { const source = (event.data as { decisionSource?: string }).decisionSource; @@ -324,6 +625,7 @@ describe("Assisted permission handling in Autopilot", async () => { try { const taskComplete = getNextEventOfType(session, "session.task_complete"); + progress.phase = "send-turn"; await session.send({ prompt: scenario.route === "shell" @@ -332,17 +634,18 @@ describe("Assisted permission handling in Autopilot", async () => { ? `ASSISTED_PATH_ROUTE: Use only glob to find *.txt in ${outsideDir} and report the matching filename.` : `ASSISTED_HUMAN_${scenario.decision.toUpperCase()}_${scenario.lifecycle.toUpperCase()}_ROUTE: Try to create the human-reviewed fixture directory once and report human-${scenario.decision === "approve" ? "approved" : "denied"}.`, }); + progress.phase = "await-task-complete"; await taskComplete; } catch (error) { throw new Error(`${JSON.stringify(scenario)} failed`, { cause: error }); } - const expectedCompletions = scenario.route === "path" ? 2 : 1; + progress.phase = "assert-results"; + const expectedCompletions = + scenario.route === "path" && scenario.lifecycle === "create" ? 2 : 1; expect(permissionCallbacks, JSON.stringify(scenario)).toBe(expectedCompletions); expect(recommendations, JSON.stringify(scenario)).toEqual( - Array(expectedCompletions).fill( - scenario.decision === "judge" ? "approve" : "requireApproval" - ) + Array(expectedCompletions).fill(expectedRecommendation) ); expect(decisionSources, JSON.stringify(scenario)).toEqual( Array(expectedCompletions).fill( @@ -387,18 +690,433 @@ describe("Assisted permission handling in Autopilot", async () => { } } finally { if (session) { + progress.phase = "abort-session"; await session.abort(); + progress.phase = "disconnect-session"; await session.disconnect(); } } } + if (group === "routing") { + progress.phase = "assert-routing-totals"; + expect(providerFailures).toEqual([]); + expect(judgeOutputs).toEqual([ + ...Array(4).fill(JUDGE_OUTPUT), + ...Array(4).fill(HUMAN_REVIEW_OUTPUT), + ]); + expect(agentCalls).toBe(scenarios.length * 2 + 4); + progress.phase = "complete"; + return; + } + + if (group === "updated authorization") { + const authorizationJudgeStart = judgeOutputs.length; + let authorizationPermissionCallbacks = 0; + const authorizationRecommendations: string[] = []; + const authorizationDecisionSources: string[] = []; + const authorizationRecoveryStatuses: string[] = []; + const authorizationToolResults: Array<{ toolCallId?: string; success?: boolean }> = []; + const authorizationTaskOutcomes: Array<{ success?: boolean; summary?: string }> = []; + let resolveAuthorizationRecommendation: (recommendation: string | undefined) => void; + const authorizationRecommendation = new Promise((resolve) => { + resolveAuthorizationRecommendation = resolve; + }); + let authorizationSession: CopilotSession | undefined; + progress.scenario = "updated-authorization"; + progress.phase = "create-session"; + try { + authorizationSession = await client.createSession({ + model: "local/gpt-5.6-sol", + providers, + models, + availableTools: [SHELL_TOOL, "task_complete"], + streaming: false, + skipCustomInstructions: true, + featureFlags: { + AUTO_APPROVAL: true, + ASSISTED_PERMISSIONS_V2: true, + }, + onPermissionRequest: async (request) => { + progress.permissionCallbacks++; + authorizationPermissionCallbacks++; + if (request.toolCallId !== "assisted-authorization-shell") { + throw new Error( + `Expected authorization shell permission, got ${String( + request.toolCallId + )}` + ); + } + const recommendation = await authorizationRecommendation; + if (recommendation !== "approve") { + throw new Error( + `Expected later authorization to reach the judge as approve, got ${String( + recommendation + )}` + ); + } + return createAttributedPermissionResult( + { kind: "approve-once" }, + { + outcome: "auto_approved", + source: "assisted_approval", + surface: "sdk", + responseCapability: "headless", + } + ); + }, + }); + progress.phase = "restriction-turn"; + await authorizationSession.sendAndWait({ + prompt: `${EARLIER_RESTRICTION_MARKER}: Do not create the authorization fixture. Reply restriction-recorded without tools.`, + }); + progress.phase = "configure-session"; + await configureAssistedAutopilot(authorizationSession, workDir); + authorizationSession.on((event) => { + recordEvent(event.type); + if (event.type === "permission.requested") { + const recommendation = ( + event.data as { + promptRequest?: { + assistedApproval?: { recommendation?: string }; + }; + } + ).promptRequest?.assistedApproval?.recommendation; + if (recommendation) { + authorizationRecommendations.push(recommendation); + } + resolveAuthorizationRecommendation(recommendation); + } else if (event.type === "permission.completed") { + const source = (event.data as { decisionSource?: string }).decisionSource; + if (source) authorizationDecisionSources.push(source); + } else if (event.type === "session.permission_recovery") { + authorizationRecoveryStatuses.push( + (event.data as { status: string }).status + ); + } else if (event.type === "tool.execution_complete") { + authorizationToolResults.push({ + toolCallId: event.data.toolCallId, + success: event.data.success, + }); + } else if (event.type === "session.task_complete") { + authorizationTaskOutcomes.push(event.data); + } + }); + + const taskComplete = getNextEventOfType( + authorizationSession, + "session.task_complete" + ); + progress.phase = "send-turn"; + await authorizationSession.send({ + prompt: `${LATER_AUTHORIZATION_MARKER}: The earlier restriction is superseded. Create the authorization fixture now and report authorization-updated.`, + }); + progress.phase = "await-task-complete"; + await taskComplete; + + progress.phase = "assert-results"; + expect(judgeOutputs.slice(authorizationJudgeStart)).toEqual([JUDGE_OUTPUT]); + expect(authorizationJudgeRequests).toHaveLength(1); + const authorizationRequest = authorizationJudgeRequests[0] ?? ""; + expect( + authorizationRequest.indexOf(EARLIER_RESTRICTION_MARKER) + ).toBeGreaterThanOrEqual(0); + expect(authorizationRequest.indexOf(LATER_AUTHORIZATION_MARKER)).toBeGreaterThan( + authorizationRequest.indexOf(EARLIER_RESTRICTION_MARKER) + ); + expect(authorizationPermissionCallbacks).toBe(1); + expect(authorizationRecommendations).toEqual(["approve"]); + expect(authorizationDecisionSources).toEqual(["assisted_approval"]); + expect(authorizationRecoveryStatuses).toEqual([]); + expect(authorizationToolResults).toContainEqual({ + toolCallId: "assisted-authorization-shell", + success: true, + }); + expect(authorizationTaskOutcomes).toEqual([ + expect.objectContaining({ + success: true, + summary: "authorization-updated", + }), + ]); + expect(existsSync(join(workDir, AUTHORIZATION_FIXTURE))).toBe(true); + } finally { + if (authorizationSession) { + progress.phase = "abort-session"; + await authorizationSession.abort(); + progress.phase = "disconnect-session"; + await authorizationSession.disconnect(); + } + } + expect(providerFailures).toEqual([]); + progress.phase = "complete"; + return; + } + + const contractScenarios = [ + { scope: "root", recommendation: "approve", handler: "registered" }, + { scope: "root", recommendation: "requireApproval", handler: "registered" }, + { scope: "root", recommendation: "excluded", handler: "registered" }, + { scope: "subagent", recommendation: "approve", handler: "registered" }, + { scope: "subagent", recommendation: "requireApproval", handler: "registered" }, + { scope: "root", recommendation: "requireApproval", handler: "none" }, + ] as const satisfies readonly { + scope: ContractScope; + recommendation: ContractRecommendation; + handler: "registered" | "none"; + }[]; + const contractJudgeStart = judgeOutputs.length; + for (const scenario of contractScenarios) { + progress.scenario = `recovery:${JSON.stringify(scenario)}`; + progress.phase = "create-session"; + let permissionCallbacks = 0; + const recommendations: string[] = []; + const recoveryStatuses: string[] = []; + const decisionSources: string[] = []; + const sequence: string[] = []; + const subagentIds = new Set(); + const permissionAgentIds: Array = []; + const toolResults: Array<{ + agentId?: string; + toolCallId?: string; + success?: boolean; + error?: { message?: string }; + result?: unknown; + }> = []; + const taskOutcomes: Array<{ success?: boolean; summary?: string }> = []; + const onPermissionRequest = () => { + progress.permissionCallbacks++; + permissionCallbacks++; + sequence.push("host"); + if (scenario.recommendation === "approve") { + return createAttributedPermissionResult( + { kind: "approve-once" }, + { + outcome: "auto_approved", + source: "assisted_approval", + surface: "sdk", + responseCapability: "headless", + } + ); + } + if (scenario.recommendation === "requireApproval") { + return createAttributedPermissionResult( + { + kind: "reject", + feedback: HUMAN_REVIEW_OUTPUT.replace(/^DENY:\s*/, ""), + }, + { + outcome: "autopilot_denied", + source: "assisted_approval", + surface: "sdk", + responseCapability: "headless", + } + ); + } + if (scenario.recommendation === "excluded") { + return createAttributedPermissionResult( + { kind: "user-not-available" }, + { + outcome: "autopilot_denied", + source: "unattended_fallback", + surface: "sdk", + responseCapability: "headless", + } + ); + } + throw new Error("Unexpected Assisted permission recommendation"); + }; + const sessionConfig = { + model: "local/gpt-5.6-sol", + providers, + models, + availableTools: [SHELL_TOOL, "task", "task_complete"], + streaming: false, + skipCustomInstructions: true, + featureFlags: { + AUTO_APPROVAL: true, + ASSISTED_PERMISSIONS_V2: true, + }, + ...(scenario.handler === "registered" ? { onPermissionRequest } : {}), + } as const; + let session: CopilotSession | undefined; + try { + session = await client.createSession(sessionConfig); + progress.phase = "configure-session"; + await configureAssistedAutopilot(session, workDir); + session.on((event) => { + recordEvent(event.type); + if (event.type === "permission.requested") { + const promptRequest = ( + event.data as { + promptRequest?: { + assistedApproval?: { recommendation?: string }; + autoApproval?: { recommendation?: string }; + }; + } + ).promptRequest; + const recommendation = + promptRequest?.assistedApproval?.recommendation ?? + promptRequest?.autoApproval?.recommendation; + if (recommendation) { + recommendations.push(recommendation); + sequence.push(`permission:${recommendation}`); + } + permissionAgentIds.push(event.agentId); + } else if (event.type === "permission.completed") { + const source = (event.data as { decisionSource?: string }).decisionSource; + if (source) decisionSources.push(source); + } else if (event.type === "session.permission_recovery") { + const status = (event.data as { status: string }).status; + recoveryStatuses.push(status); + sequence.push(`recovery:${status}`); + } else if (event.type === "tool.execution_complete") { + toolResults.push({ + agentId: event.agentId, + toolCallId: event.data.toolCallId, + success: event.data.success, + error: event.data.error, + result: event.data.result, + }); + } else if (event.type === "session.task_complete") { + taskOutcomes.push(event.data); + } else if (event.type === "subagent.started") { + subagentIds.add(event.agentId); + } + }); + + const marker = contractMarker(scenario.recommendation); + const prompt = + scenario.scope === "root" + ? `ASSISTED_CONTRACT_ROOT_${marker}: Run the requested shell command once and report the outcome.` + : `ASSISTED_CONTRACT_SUBAGENT_${marker}: Use the task tool once so a task subagent runs the requested shell command.`; + try { + const taskComplete = getNextEventOfType(session, "session.task_complete"); + progress.phase = "send-turn"; + await session.send({ prompt }); + progress.phase = "await-task-complete"; + await taskComplete; + } catch (error) { + throw new Error(`${JSON.stringify(scenario)} failed`, { cause: error }); + } + + progress.phase = "assert-results"; + if (scenario.handler === "none") { + expect(permissionCallbacks, JSON.stringify(scenario)).toBe(0); + expect(recommendations, JSON.stringify(scenario)).toEqual([]); + } else if (scenario.recommendation === "approve") { + expect(permissionCallbacks, JSON.stringify(scenario)).toBe(1); + expect(recommendations, JSON.stringify(scenario)).toEqual(["approve"]); + } else if (scenario.recommendation === "requireApproval") { + expect(permissionCallbacks, JSON.stringify(scenario)).toBe(1); + expect(recommendations, JSON.stringify(scenario)).toEqual(["requireApproval"]); + } else { + expect(permissionCallbacks, JSON.stringify(scenario)).toBeGreaterThanOrEqual(1); + expect(recommendations, JSON.stringify(scenario)).toEqual( + Array(permissionCallbacks).fill("excluded") + ); + } + if (scenario.handler === "none") { + expect(decisionSources, JSON.stringify(scenario)).toEqual([]); + expect(recoveryStatuses, JSON.stringify(scenario)).toEqual(["recovering"]); + } else { + expect(decisionSources, JSON.stringify(scenario)).toContain( + scenario.recommendation === "excluded" + ? "unattended_fallback" + : "assisted_approval" + ); + } + if (scenario.recommendation === "excluded") { + expect(recoveryStatuses.at(-1), JSON.stringify(scenario)).toBe("blocked"); + } else if (scenario.handler === "registered") { + expect(recoveryStatuses, JSON.stringify(scenario)).toEqual([]); + } + const hostIndex = sequence.indexOf("host"); + const firstRecoveryIndex = sequence.findIndex((entry) => + entry.startsWith("recovery:") + ); + if (firstRecoveryIndex !== -1 && scenario.handler === "registered") { + expect( + hostIndex, + JSON.stringify({ scenario, sequence }) + ).toBeGreaterThanOrEqual(0); + expect(hostIndex, JSON.stringify({ scenario, sequence })).toBeLessThan( + firstRecoveryIndex + ); + } + + const shellResult = toolResults.find( + (result) => + result.toolCallId === + contractShellCallId(scenario.scope, scenario.recommendation) + ); + expect(shellResult, JSON.stringify({ scenario, toolResults })).toBeDefined(); + expect(shellResult?.success, JSON.stringify(scenario)).toBe( + scenario.recommendation === "approve" && scenario.handler === "registered" + ); + if ( + scenario.recommendation === "requireApproval" && + scenario.handler === "registered" + ) { + expect(shellResult?.error?.message, JSON.stringify(scenario)).toContain( + "Ask the registered human permission handler." + ); + } else if (scenario.handler === "none") { + expect(shellResult?.error?.message, JSON.stringify(scenario)).toContain( + "Permission could not be granted automatically." + ); + } + if (scenario.scope === "subagent") { + expect(subagentIds.size, JSON.stringify(scenario)).toBe(1); + expect( + subagentIds.has(shellResult?.agentId ?? ""), + JSON.stringify(scenario) + ).toBe(true); + for (const agentId of permissionAgentIds) { + expect(subagentIds.has(agentId ?? ""), JSON.stringify(scenario)).toBe(true); + } + } + + expect( + existsSync( + join(workDir, contractFixtureName(scenario.scope, scenario.recommendation)) + ), + JSON.stringify(scenario) + ).toBe(scenario.recommendation === "approve" && scenario.handler === "registered"); + progress.phase = "read-pending-requests"; + expect( + (await session.rpc.permissions.pendingRequests()).items, + JSON.stringify(scenario) + ).toEqual([]); + expect(taskOutcomes, JSON.stringify(scenario)).toHaveLength(1); + expect(taskOutcomes[0]?.success, JSON.stringify(scenario)).toBe( + scenario.recommendation !== "excluded" && scenario.handler === "registered" + ); + if (scenario.handler === "none") { + expect(taskOutcomes[0], JSON.stringify(scenario)).toMatchObject({ + outcome: "continue", + reason: "Autopilot is still recovering from a required permission.", + }); + } + } finally { + if (session) { + progress.phase = "abort-session"; + await session.abort(); + progress.phase = "disconnect-session"; + await session.disconnect(); + } + } + } + + progress.phase = "assert-recovery-totals"; expect(providerFailures).toEqual([]); - expect(judgeOutputs).toEqual([ - ...Array(4).fill(JUDGE_OUTPUT), - ...Array(4).fill(HUMAN_REVIEW_OUTPUT), + expect(judgeOutputs.slice(contractJudgeStart)).toEqual([ + JUDGE_OUTPUT, + HUMAN_REVIEW_OUTPUT, + JUDGE_OUTPUT, + HUMAN_REVIEW_OUTPUT, + HUMAN_REVIEW_OUTPUT, ]); - expect(agentCalls).toBe(scenarios.length * 2 + 4); + progress.phase = "complete"; }); }); @@ -422,3 +1140,15 @@ async function configureAssistedAutopilot(session: CopilotSession, workDir: stri }); await session.rpc.mode.set({ mode: "autopilot" }); } + +async function expectAssistedAutopilotConfigured( + session: CopilotSession, + workDir: string +): Promise { + expect(await session.rpc.mode.get()).toBe("autopilot"); + expect(await session.rpc.permissions.getMode()).toMatchObject({ mode: "assisted" }); + expect((await session.rpc.permissions.folderTrust.isTrusted({ path: workDir })).trusted).toBe( + true + ); + expect((await session.rpc.permissions.paths.list()).primary).toBe(await realpath(workDir)); +} diff --git a/nodejs/test/e2e/auto_fusion_routing.e2e.test.ts b/nodejs/test/e2e/auto_fusion_routing.e2e.test.ts new file mode 100644 index 0000000000..798b450028 --- /dev/null +++ b/nodejs/test/e2e/auto_fusion_routing.e2e.test.ts @@ -0,0 +1,904 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { beforeEach, describe, expect, it, onTestFailed } from "vitest"; +import { approveAll, CopilotRequestHandler, type SessionEvent } from "../../src/index.js"; +import { createSdkTestContext, DEFAULT_GITHUB_TOKEN } from "./harness/sdkTestContext.js"; +import { isByokBackend } from "./harness/testBackend.js"; + +const MODEL = "gpt-5.6-sol"; +const REPLY = "AUTO_FUSION_EXECUTION_DONE"; + +class AutoFusionRequestHandler extends CopilotRequestHandler { + readonly plans: Record[] = []; + readonly auto: Record[] = []; + readonly inference: Record[] = []; + failRoute = false; + disabled = false; + constituentsEnabled = true; + inferenceGate?: { + entered: ReturnType>; + release: ReturnType>; + }; + + constructor( + private readonly planVersion: "1" | "2", + private readonly executionModel = MODEL + ) { + super(); + } + + protected override async sendRequest(request: Request): Promise { + const path = new URL(request.url).pathname; + const models = { + data: [...new Set([MODEL, this.executionModel])].map((id) => ({ + id, + name: id, + model_picker_enabled: this.constituentsEnabled, + policy: { state: this.constituentsEnabled ? "enabled" : "disabled" }, + supported_endpoints: ["/chat/completions"], + capabilities: { + supports: { streaming: true, tool_calls: true, vision: true }, + limits: { + max_prompt_tokens: 128000, + max_output_tokens: 16384, + max_context_window_tokens: 144384, + }, + }, + })), + }; + if (path === "/models") return Response.json(models); + if (path === "/meta") { + return Response.json({ + models, + auto: { + default_tier: "Balance-V2", + tiers: [ + { + id: "Balance-V2", + display_name: "Balance", + description: "", + type: "auto", + status: { enabled: true }, + }, + { + id: "Other-V2", + display_name: "Ordinary Auto", + description: "", + type: "auto", + status: { enabled: !this.disabled, message: "Contact admin" }, + }, + ...["hydrafusion", "hydrafusion-max"].map((id) => ({ + id, + display_name: id, + description: "", + type: "fusion", + status: { enabled: !this.disabled, message: "Contact admin" }, + })), + { + id: "future-fusion", + display_name: "Future", + description: "", + type: "fusion", + status: { enabled: true }, + }, + { + id: "future-type", + display_name: "Unknown", + description: "", + type: "ensemble", + status: { enabled: true }, + }, + ], + }, + }); + } + if (path === "/model/fusion") { + const body = (await request.json()) as Record; + this.plans.push(body); + if (this.failRoute) + return Response.json({ message: "Route unavailable" }, { status: 500 }); + return Response.json({ + fusion_mode: body.fusion_mode, + fusion_pattern: "solo", + plan_version: this.planVersion, + steps: [{ role: "generation", model_id: this.executionModel }], + session: { token: "jwt.fake.auto.fusion.plan", expires_at: 0 }, + }); + } + if (path === "/auto") { + this.auto.push((await request.json()) as Record); + return Response.json({ + selected_model: models.data[0], + session_token: "jwt.fake.auto.fusion.reset", + expires_at: Math.floor(Date.now() / 1000) + 3600, + }); + } + if (path === "/chat/completions") { + const body = (await request.json()) as Record; + this.inference.push(body); + const gate = this.inferenceGate; + if (gate) { + gate.entered.resolve(); + await gate.release.promise; + } + if (body.model !== MODEL && body.model !== this.executionModel) + throw new Error(`Synthetic model escaped to inference: ${body.model}`); + const completion = { + id: "chatcmpl-auto-fusion", + object: "chat.completion", + created: 0, + model: body.model, + choices: [ + { + index: 0, + message: { role: "assistant", content: REPLY }, + finish_reason: "stop", + }, + ], + usage: { prompt_tokens: 11, completion_tokens: 7, total_tokens: 18 }, + }; + if (!body.stream) return Response.json(completion); + return new Response( + `data: ${JSON.stringify({ + ...completion, + object: "chat.completion.chunk", + choices: [ + { + index: 0, + delta: { role: "assistant", content: REPLY }, + finish_reason: "stop", + }, + ], + })}\n\ndata: [DONE]\n\n`, + { headers: { "content-type": "text/event-stream" } } + ); + } + throw new Error(`Unexpected routing request: ${request.method} ${path}`); + } +} + +describe.skipIf(isByokBackend).each([ + { mode: "hydrafusion", version: "1" as const }, + { mode: "hydrafusion", version: "2" as const }, + { mode: "hydrafusion-max", version: "2" as const }, +])("Metadata Auto execution: $mode v$version", async ({ mode, version }) => { + const executionModel = mode === "hydrafusion" ? "claude-sonnet-5" : MODEL; + const handler = new AutoFusionRequestHandler(version, executionModel); + const { + copilotClient: client, + createClient, + workDir, + } = await createSdkTestContext({ + useStdio: true, + copilotClientOptions: { + gitHubToken: DEFAULT_GITHUB_TOKEN, + requestHandler: handler, + env: { + DYNAMIC_AUTO_TIERS: "false", + COPILOT_EXP_COPILOT_CLI_DYNAMIC_AUTO_TIERS: "false", + HYDRAFUSION: "false", + HYDRAFUSION_ROLLOUT: "false", + COPILOT_EXP_COPILOT_CLI_HYDRAFUSION_ROLLOUT: "false", + HYDRAFUSION_PLAN_V2: String(version === "2"), + COPILOT_EXP_COPILOT_CLI_HYDRAFUSION_PLAN_V2: String(version === "2"), + }, + }, + }); + const config = { + model: "auto", + workingDirectory: workDir, + expAssignments: { + Features: [], + Flights: {}, + Configs: [{ Id: "default", Parameters: { copilot_cli_dynamic_auto_tiers: true } }], + AssignmentContext: "metadata-fusion-treatment", + }, + capi: { enableWebSocketResponses: false }, + onPermissionRequest: approveAll, + }; + beforeEach(() => { + handler.plans.length = 0; + handler.auto.length = 0; + handler.inference.length = 0; + handler.failRoute = false; + handler.disabled = false; + handler.constituentsEnabled = true; + handler.inferenceGate = undefined; + }); + + it("routes only the selected preference, commits it, resumes cold and resets to Auto", async () => { + const diagnosticStart = performance.now(); + const phases: { phase: string; elapsedMs: number }[] = []; + const markPhase = (phase: string) => + phases.push({ phase, elapsedMs: Math.round(performance.now() - diagnosticStart) }); + onTestFailed(() => { + process.stderr.write( + `${JSON.stringify({ + test: "metadata Auto create/resume/reset", + mode, + version, + phases, + planCount: handler.plans.length, + autoCount: handler.auto.length, + inferenceCount: handler.inference.length, + })}\n` + ); + }); + markPhase("creating session"); + const session = await client.createSession(config); + markPhase("listing models"); + const catalog = await session.rpc.model.list(); + expect(catalog.auto?.tiers.some((tier) => tier.id === mode)).toBe(true); + expect( + catalog.list.some( + (model) => + typeof model === "object" && + model !== null && + "id" in model && + typeof model.id === "string" && + model.id.startsWith("hydrafusion") + ) + ).toBe(false); + expect(handler.plans).toEqual([]); + await expect(session.setAutoTier("future-fusion")).rejects.toThrow("unsupported execution"); + await expect(session.setAutoTier("future-type")).rejects.toThrow("unsupported execution"); + await expect(session.setAutoTier("HydraFusion")).rejects.toThrow("unavailable"); + const pending = await session.setAutoTier(mode); + markPhase("first turn"); + expect(pending.pendingAutoTier).toBe(mode); + expect((await session.rpc.model.getCurrent()).autoTier).toBeUndefined(); + const events: SessionEvent[] = []; + session.on((event) => events.push(event)); + expect( + ( + await session.sendAndWait({ + prompt: "What is the chemical symbol for gold? Do not use tools.", + }) + )?.data.content + ).toBe(REPLY); + expect(handler.plans).toEqual([ + expect.objectContaining({ + fusion_mode: mode, + ...(version === "2" ? { plan_version: version } : {}), + }), + ]); + if (version === "1") expect(handler.plans[0]).not.toHaveProperty("plan_version"); + expect(handler.auto).toEqual([]); + expect(events.some((event) => event.type === "session.fusion_route_failed")).toBe(false); + expect(events).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + type: "session.fusion_completed", + data: expect.objectContaining({ syntheticModel: mode }), + }), + ]) + ); + expect(events).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + type: "session.fusion_resolved", + data: expect.objectContaining({ + syntheticModel: mode, + policy: mode === "hydrafusion" ? "standard" : "max", + primaryModel: executionModel, + }), + }), + ]) + ); + expect(await session.rpc.model.getCurrent()).toMatchObject({ + modelId: "auto", + autoTier: mode, + }); + const sessionId = session.sessionId; + markPhase("cold client shutdown"); + await session.disconnect(); + await client.stop(); + const coldClient = createClient({ requestHandler: handler }); + try { + markPhase("cold resume"); + const resumed = await coldClient.resumeSession(sessionId, config); + markPhase("cold structured-output admission"); + expect(await resumed.rpc.model.getCurrent()).toMatchObject({ + modelId: "auto", + autoTier: mode, + }); + await expect( + resumed.sendAndWait({ + prompt: "Return a structured chemical symbol before the resumed turn.", + responseSchema: { type: "object", properties: { symbol: { type: "string" } } }, + }) + ).rejects.toThrow("responseFormat is not supported"); + expect(handler.plans).toHaveLength(1); + + markPhase("resumed turn"); + expect( + ( + await resumed.sendAndWait({ + prompt: "What is the chemical symbol for silver? Do not use tools.", + }) + )?.data.content + ).toBe(REPLY); + expect(handler.plans).toHaveLength(2); + markPhase("switch concrete then auto"); + await resumed.setModel(MODEL); + expect((await resumed.rpc.model.getCurrent()).autoTier).toBe(mode); + await resumed.setModel("auto"); + expect( + ( + await resumed.sendAndWait({ + prompt: "Name the chemical symbol for iron. Do not use tools.", + }) + )?.data.content + ).toBe(REPLY); + expect(handler.plans).toHaveLength(3); + markPhase("switch ordinary auto"); + await resumed.setModel("auto", { autoTier: "Balance-V2" }); + expect((await resumed.rpc.model.getCurrent()).autoTier).toBe(mode); + expect( + ( + await resumed.sendAndWait({ + prompt: "What is the chemical symbol for copper? Do not use tools.", + }) + )?.data.content + ).toBe(REPLY); + expect(handler.plans).toHaveLength(3); + expect(handler.auto.at(-1)).toMatchObject({ tier: "Balance-V2" }); + expect(await resumed.rpc.model.getCurrent()).toMatchObject({ + modelId: "auto", + autoTier: "Balance-V2", + }); + await resumed.setAutoTier(null); + markPhase("reset turn"); + expect( + ( + await resumed.sendAndWait({ + prompt: "Name the chemical symbol for lead. Do not use tools.", + }) + )?.data.content + ).toBe(REPLY); + expect((await resumed.rpc.model.getCurrent()).autoTier).toBeUndefined(); + expect(handler.plans).toHaveLength(3); + await resumed.disconnect(); + } finally { + markPhase("cold client cleanup"); + await coldClient.stop(); + } + markPhase("complete"); + }); + + it("executes explicit create and atomic Auto selection without rewriting the logical model", async () => { + const created = await client.createSession({ + ...config, + capi: { ...config.capi, autoTier: mode }, + }); + expect( + ( + await created.sendAndWait({ + prompt: "Name the chemical symbol for gold. Do not use tools.", + }) + )?.data.content + ).toBe(REPLY); + expect(await created.rpc.model.getCurrent()).toMatchObject({ + modelId: "auto", + autoTier: mode, + }); + await created.disconnect(); + const switched = await client.createSession({ ...config, model: MODEL }); + await switched.setModel("auto", { autoTier: mode }); + expect((await switched.rpc.model.getCurrent()).pendingAutoTier).toBe(mode); + expect( + ( + await switched.sendAndWait({ + prompt: "Name the chemical symbol for silver. Do not use tools.", + }) + )?.data.content + ).toBe(REPLY); + expect(await switched.rpc.model.getCurrent()).toMatchObject({ + modelId: "auto", + autoTier: mode, + }); + expect(handler.plans).toHaveLength(2); + expect( + handler.plans.every( + (plan) => + plan.fusion_mode === mode && + plan.plan_version === (version === "2" ? version : undefined) + ) + ).toBe(true); + expect(handler.auto).toEqual([]); + await switched.disconnect(); + }); + + it("retains typed Fusion route-failure fallback and commits only the concrete response", async () => { + handler.failRoute = true; + const session = await client.createSession(config); + await session.setAutoTier(mode); + const events: SessionEvent[] = []; + session.on((event) => events.push(event)); + expect( + ( + await session.sendAndWait({ + prompt: "What is the chemical symbol for gold? Do not use tools.", + }) + )?.data.content + ).toBe(REPLY); + expect(events.some((event) => event.type === "session.fusion_route_failed")).toBe(true); + expect(events.filter((event) => event.type === "assistant.message")).toHaveLength(1); + expect(await session.rpc.model.getCurrent()).toMatchObject({ + modelId: "auto", + autoTier: mode, + }); + expect(handler.plans).toHaveLength(1); + expect(handler.auto).toEqual([]); + await session.disconnect(); + }); + + it("applies reasoning-effort constraints consistently to dedicated Fusion setters", async () => { + const session = await client.createSession(config); + await session.rpc.model.setReasoningEffort({ reasoningEffort: "high" }); + await expect(session.setAutoTier(mode)).rejects.toThrow("do not support reasoning effort"); + expect((await session.rpc.model.getCurrent()).reasoningEffort).toBe("high"); + expect((await session.rpc.model.getCurrent()).pendingAutoTier).toBeUndefined(); + await session.rpc.model.setReasoningEffort({ reasoningEffort: "none" }); + await session.setAutoTier(mode); + await expect( + session.rpc.model.setReasoningEffort({ reasoningEffort: "high" }) + ).rejects.toThrow("do not support reasoning effort"); + await session.rpc.model.setReasoningEffort({ reasoningEffort: "none" }); + await session.setAutoTier(null); + await session.rpc.model.setReasoningEffort({ reasoningEffort: "high" }); + expect((await session.rpc.model.getCurrent()).reasoningEffort).toBe("high"); + await session.disconnect(); + }); + + it("retains the staged Fusion preference when the first inference is cancelled", async () => { + const session = await client.createSession({ + ...config, + capi: { ...config.capi, autoTier: "Balance-V2" }, + }); + const events: SessionEvent[] = []; + session.on((event) => events.push(event)); + await session.setAutoTier(mode); + const gate = { + entered: Promise.withResolvers(), + release: Promise.withResolvers(), + }; + handler.inferenceGate = gate; + const turn = session + .sendAndWait({ + prompt: "What is the chemical symbol for gold? Do not use tools.", + }) + .then( + (reply) => ({ reply, error: undefined }), + (error: Error) => ({ reply: undefined, error }) + ); + try { + await gate.entered.promise; + expect((await session.rpc.model.getCurrent()).activatingAutoTier).toBe(mode); + await session.abort(); + } finally { + gate.release.resolve(); + handler.inferenceGate = undefined; + } + await turn; + expect(await session.rpc.model.getCurrent()).toMatchObject({ + modelId: "auto", + autoTier: "Balance-V2", + pendingAutoTier: mode, + }); + expect((await session.rpc.model.getCurrent()).activatingAutoTier).toBeUndefined(); + expect(events.some((event) => event.type === "session.auto_tier_switch_failed")).toBe( + false + ); + expect( + ( + await session.sendAndWait({ + prompt: "What is the chemical symbol for silver? Do not use tools.", + }) + )?.data.content + ).toBe(REPLY); + expect(handler.plans).toHaveLength(2); + expect(handler.auto).toEqual([]); + expect(await session.rpc.model.getCurrent()).toMatchObject({ + modelId: "auto", + autoTier: mode, + }); + await session.disconnect(); + }); + + it.each(["fusion", "auto"] as const)( + "settles an invalidated pending %s preference once and preserves the incumbent", + async (execution) => { + await client.stop(); + const session = await client.createSession({ + ...config, + capi: { ...config.capi, autoTier: "Balance-V2" }, + }); + const events: SessionEvent[] = []; + session.on((event) => events.push(event)); + const requested = execution === "fusion" ? mode : "Other-V2"; + await session.setAutoTier(requested); + expect((await session.rpc.model.getCurrent()).pendingAutoTier).toBe(requested); + handler.disabled = true; + await session.rpc.model.list({ skipCache: true }); + await session + .sendAndWait({ + prompt: "What is the chemical symbol for gold? Do not use tools.", + }) + .then( + (reply) => expect(reply?.data.content).toBe(REPLY), + (error: unknown) => { + expect(error).toBeInstanceOf(Error); + expect((error as Error).message).toContain("Contact admin"); + } + ); + const current = await session.rpc.model.getCurrent(); + expect(current.autoTier).toBe("Balance-V2"); + expect(current.pendingAutoTier).toBeUndefined(); + expect(current.activatingAutoTier).toBeUndefined(); + expect( + events.filter((event) => event.type === "session.auto_tier_switch_failed") + ).toEqual([ + expect.objectContaining({ + data: expect.objectContaining({ + requestedAutoTier: requested, + effectiveAutoTier: "Balance-V2", + reason: "unsupported", + }), + }), + ]); + expect( + ( + await session.sendAndWait({ + prompt: "What is the chemical symbol for silver? Do not use tools.", + }) + )?.data.content + ).toBe(REPLY); + expect(handler.plans).toEqual([]); + expect(handler.auto.every((body) => body.tier === "Balance-V2")).toBe(true); + expect( + events.filter((event) => event.type === "session.auto_tier_switch_failed") + ).toHaveLength(1); + await session.disconnect(); + } + ); + + it("reports setup failure after claiming Fusion and clears only the failed request", async () => { + await client.stop(); + const session = await client.createSession({ + ...config, + capi: { ...config.capi, autoTier: "Balance-V2" }, + }); + const events: SessionEvent[] = []; + const failedTurnIdle = Promise.withResolvers(); + let failureObserved = false; + session.on((event) => { + events.push(event); + if (event.type === "session.error") failureObserved = true; + if (event.type === "session.idle" && failureObserved) failedTurnIdle.resolve(); + }); + await session.setAutoTier(mode); + handler.constituentsEnabled = false; + await session.rpc.model.list({ skipCache: true }); + await expect( + session.sendAndWait({ + prompt: "What is the chemical symbol for gold? Do not use tools.", + }) + ).rejects.toThrow("no executable constituent"); + // sendAndWait rejects on session.error before that failed turn's idle tail settles. + await failedTurnIdle.promise; + // Host-forwarded error/idle can overtake this event's recorded-stream delivery. + await expect + .poll(() => events.filter((event) => event.type === "session.auto_tier_switch_failed")) + .toEqual([ + expect.objectContaining({ + data: expect.objectContaining({ + requestedAutoTier: mode, + effectiveAutoTier: "Balance-V2", + reason: "setup_failed", + }), + }), + ]); + expect(await session.rpc.model.getCurrent()).toMatchObject({ + modelId: "auto", + autoTier: "Balance-V2", + }); + expect((await session.rpc.model.getCurrent()).pendingAutoTier).toBeUndefined(); + expect((await session.rpc.model.getCurrent()).activatingAutoTier).toBeUndefined(); + expect(handler.plans).toEqual([]); + expect(handler.inference).toEqual([]); + handler.constituentsEnabled = true; + await session.rpc.model.list({ skipCache: true }); + expect( + ( + await session.sendAndWait({ + prompt: "What is the chemical symbol for silver? Do not use tools.", + }) + )?.data.content + ).toBe(REPLY); + expect(handler.plans).toEqual([]); + expect( + events.filter((event) => event.type === "session.auto_tier_switch_failed") + ).toHaveLength(1); + await session.disconnect(); + }); + + it("rejects unsupported context and structured output before Fusion activation", async () => { + const session = await client.createSession(config); + await expect( + session.rpc.model.switchTo({ + modelId: "auto", + autoTier: mode, + contextTier: "long", + }) + ).rejects.toThrow("do not support context tiers"); + expect((await session.rpc.model.getCurrent()).autoTier).toBeUndefined(); + await session.setAutoTier(mode); + await expect( + session.sendAndWait({ + prompt: "Return the chemical symbol for gold as structured JSON.", + responseSchema: { + type: "object", + properties: { symbol: { type: "string" } }, + required: ["symbol"], + additionalProperties: false, + }, + }) + ).rejects.toThrow("responseFormat is not supported"); + expect(await session.rpc.model.getCurrent()).toMatchObject({ + modelId: "auto", + pendingAutoTier: mode, + }); + expect((await session.rpc.model.getCurrent()).autoTier).toBeUndefined(); + expect(handler.plans).toEqual([]); + expect(handler.auto).toEqual([]); + expect(handler.inference).toEqual([]); + expect((await session.rpc.queue.pendingItems()).items).toEqual([]); + expect( + (await session.getEvents()).filter( + (event) => event.type === "user.message" || event.type === "session.error" + ) + ).toEqual([]); + await session.disconnect(); + }); +}); + +describe.skipIf(isByokBackend).each(["1", "2"] as const)( + "Manual HydraFusion plan-version control: %s", + async (version) => { + const handler = new AutoFusionRequestHandler(version); + const { copilotClient: client, workDir } = await createSdkTestContext({ + useStdio: true, + copilotClientOptions: { + gitHubToken: DEFAULT_GITHUB_TOKEN, + requestHandler: handler, + env: { + DYNAMIC_AUTO_TIERS: "false", + COPILOT_EXP_COPILOT_CLI_DYNAMIC_AUTO_TIERS: "false", + HYDRAFUSION: "true", + HYDRAFUSION_ROLLOUT: "true", + HYDRAFUSION_PLAN_V2: String(version === "2"), + COPILOT_EXP_COPILOT_CLI_HYDRAFUSION_PLAN_V2: String(version === "2"), + }, + }, + }); + it("retains the manual v1 Max and v2 Standard execution mapping", async () => { + const session = await client.createSession({ + model: "hydrafusion", + enableExperimentalMode: true, + workingDirectory: workDir, + capi: { enableWebSocketResponses: false }, + onPermissionRequest: approveAll, + }); + const events: SessionEvent[] = []; + session.on((event) => events.push(event)); + expect( + ( + await session.sendAndWait({ + prompt: "What is the chemical symbol for gold? Do not use tools.", + }) + )?.data.content + ).toBe(REPLY); + expect(handler.plans).toEqual([ + expect.objectContaining({ + fusion_mode: version === "1" ? "hydrafusion-max" : "hydrafusion", + ...(version === "2" ? { plan_version: "2" } : {}), + }), + ]); + expect(events).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + type: "session.fusion_resolved", + data: expect.objectContaining({ + policy: version === "1" ? "max" : "standard", + }), + }), + ]) + ); + expect(events.some((event) => event.type === "session.fusion_route_failed")).toBe( + false + ); + expect((await session.rpc.model.getCurrent()).modelId).toBe("hydrafusion"); + await session.disconnect(); + }); + + if (version === "1") { + it("routes metadata Standard independently of manual Max history and resumes same-policy reuse", async () => { + const planStart = handler.plans.length; + const config = { + model: "hydrafusion", + enableExperimentalMode: true, + workingDirectory: workDir, + capi: { enableWebSocketResponses: false }, + onPermissionRequest: approveAll, + expAssignments: { + Features: [], + Flights: {}, + Configs: [ + { + Id: "default", + Parameters: { + copilot_cli_dynamic_auto_tiers: true, + copilot_cli_hydrafusion_turn_policy: "initial_only", + }, + }, + ], + AssignmentContext: "policy-scoped-fusion-history", + }, + }; + const session = await client.createSession(config); + const events: SessionEvent[] = []; + session.on((event) => events.push(event)); + expect( + ( + await session.sendAndWait({ + prompt: "Name the chemical symbol for gold. Do not use tools.", + }) + )?.data.content + ).toBe(REPLY); + expect(handler.plans.slice(planStart)).toEqual([ + expect.objectContaining({ fusion_mode: "hydrafusion-max" }), + ]); + await session.setModel("auto", { autoTier: "hydrafusion" }); + expect( + ( + await session.sendAndWait({ + prompt: "Name the chemical symbol for silver. Do not use tools.", + }) + )?.data.content + ).toBe(REPLY); + expect(handler.plans.slice(planStart)).toEqual([ + expect.objectContaining({ fusion_mode: "hydrafusion-max" }), + expect.objectContaining({ fusion_mode: "hydrafusion" }), + ]); + expect( + events + .filter((event) => event.type === "session.fusion_resolved") + .map((event) => event.data.policy) + ).toEqual(["max", "standard"]); + expect(await session.rpc.model.getCurrent()).toMatchObject({ + modelId: "auto", + autoTier: "hydrafusion", + }); + const sessionId = session.sessionId; + await session.disconnect(); + await client.stop(); + const resumed = await client.resumeSession(sessionId, { + ...config, + model: "auto", + }); + expect( + ( + await resumed.sendAndWait({ + prompt: "Name the chemical symbol for copper. Do not use tools.", + }) + )?.data.content + ).toBe(REPLY); + expect(handler.plans.slice(planStart)).toHaveLength(2); + expect(await resumed.rpc.model.getCurrent()).toMatchObject({ + modelId: "auto", + autoTier: "hydrafusion", + }); + await resumed.disconnect(); + }); + } + } +); + +describe.skipIf(isByokBackend)("Metadata Fusion rejection before activation", async () => { + const handler = new AutoFusionRequestHandler("1"); + const { copilotClient: client, workDir } = await createSdkTestContext({ + useStdio: true, + copilotClientOptions: { + gitHubToken: DEFAULT_GITHUB_TOKEN, + requestHandler: handler, + env: { + DYNAMIC_AUTO_TIERS: "true", + HYDRAFUSION_PLAN_V2: "false", + COPILOT_EXP_COPILOT_CLI_HYDRAFUSION_PLAN_V2: "false", + }, + }, + }); + + it("rejects Max with actionable plan-v2 guidance without changing incumbent or calling endpoints", async () => { + const config = { + model: "auto", + workingDirectory: workDir, + capi: { autoTier: "Balance-V2" }, + onPermissionRequest: approveAll, + }; + await expect( + client.createSession({ ...config, capi: { autoTier: "hydrafusion-max" } }) + ).rejects.toThrow("HYDRAFUSION_PLAN_V2"); + const session = await client.createSession(config); + await expect(session.setAutoTier("hydrafusion-max")).rejects.toThrow("HYDRAFUSION_PLAN_V2"); + await expect(session.setModel("auto", { autoTier: "hydrafusion-max" })).rejects.toThrow( + "HYDRAFUSION_PLAN_V2" + ); + expect(await session.rpc.model.getCurrent()).toMatchObject({ + modelId: "auto", + autoTier: "Balance-V2", + }); + expect((await session.rpc.model.getCurrent()).pendingAutoTier).toBeUndefined(); + expect(handler.plans).toEqual([]); + expect(handler.auto).toEqual([]); + expect(handler.inference).toEqual([]); + handler.disabled = true; + await session.rpc.model.list({ skipCache: true }); + await expect(session.setAutoTier("hydrafusion")).rejects.toThrow("Contact admin"); + await expect(session.setAutoTier("Other-V2")).rejects.toThrow("Contact admin"); + expect((await session.rpc.model.getCurrent()).autoTier).toBe("Balance-V2"); + await session.disconnect(); + }); + + it("does not settle pending Auto intent when structured-output admission rejects metadata", async () => { + await client.stop(); + handler.disabled = false; + const session = await client.createSession({ + model: "auto", + workingDirectory: workDir, + capi: { autoTier: "Balance-V2", enableWebSocketResponses: false }, + onPermissionRequest: approveAll, + }); + await session.setAutoTier("Other-V2"); + handler.disabled = true; + await session.rpc.model.list({ skipCache: true }); + const before = await session.getEvents(); + await expect( + session.sendAndWait({ + prompt: "Return the chemical symbol for gold as structured JSON.", + responseSchema: { + type: "object", + properties: { symbol: { type: "string" } }, + }, + }) + ).rejects.toThrow("Contact admin"); + expect(await session.rpc.model.getCurrent()).toMatchObject({ + modelId: "auto", + autoTier: "Balance-V2", + pendingAutoTier: "Other-V2", + }); + expect((await session.rpc.model.getCurrent()).activatingAutoTier).toBeUndefined(); + expect((await session.rpc.queue.pendingItems()).items).toEqual([]); + expect(await session.getEvents()).toEqual(before); + expect(handler.plans).toEqual([]); + expect(handler.inference).toEqual([]); + handler.disabled = false; + await session.rpc.model.list({ skipCache: true }); + expect( + ( + await session.sendAndWait({ + prompt: "What is the chemical symbol for silver? Do not use tools.", + }) + )?.data.content + ).toBe(REPLY); + expect(handler.auto.at(-1)).toMatchObject({ tier: "Other-V2" }); + expect(await session.rpc.model.getCurrent()).toMatchObject({ + modelId: "auto", + autoTier: "Other-V2", + }); + await session.disconnect(); + }); +}); diff --git a/nodejs/test/e2e/browser_shadow_reload.e2e.test.ts b/nodejs/test/e2e/browser_shadow_reload.e2e.test.ts new file mode 100644 index 0000000000..82a005528d --- /dev/null +++ b/nodejs/test/e2e/browser_shadow_reload.e2e.test.ts @@ -0,0 +1,143 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { dirname, join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { describe, expect, it } from "vitest"; +import { approveAll, RuntimeConnection } from "../../src/index.js"; +import { createSdkTestContext } from "./harness/sdkTestContext.js"; +import { waitForCondition } from "./harness/sdkTestHelper.js"; + +const BROWSER_NAME = "github-copilot-browser"; +const SHADOW_SERVER = "installed-browser-shadow"; +const TEST_MCP_SERVER = resolve( + dirname(fileURLToPath(import.meta.url)), + "../../../test/harness/test-mcp-server.mjs" +); + +describe("Browser shadow reload", async () => { + const { createClient, workDir, env } = await createSdkTestContext({ useStdio: true }); + + it( + "observes another process's install, disable and removal on a warm session", + { timeout: 90_000 }, + async () => { + const bundle = join(workDir, "bundle"); + const shadow = join(workDir, "shadow"); + for (const [directory, server] of [ + [bundle, BROWSER_NAME], + [shadow, SHADOW_SERVER], + ]) { + mkdirSync(join(directory, ".plugin"), { recursive: true }); + writeFileSync( + join(directory, ".plugin", "plugin.json"), + JSON.stringify({ + name: BROWSER_NAME, + version: "0.0.3", + mcpServers: "./.mcp.json", + }) + ); + writeFileSync( + join(directory, ".mcp.json"), + JSON.stringify({ + mcpServers: { + [server]: { + command: process.execPath, + args: [TEST_MCP_SERVER], + tools: ["*"], + }, + }, + }) + ); + } + writeFileSync( + join(env.COPILOT_HOME, "settings.json"), + JSON.stringify({ enabledPlugins: { [BROWSER_NAME]: true }, disableAllHooks: true }) + ); + await using owner = createClient({ + connection: RuntimeConnection.forStdio(), + builtinPluginDirectories: [bundle], + }); + await using writer = createClient({ connection: RuntimeConnection.forStdio() }); + await owner.start(); + await writer.start(); + await writer.rpc.plugins.builtin.set({ paths: [] }); + // Exercise persisted MCP discovery, not a host-owned effective-plugin snapshot. + const session = await owner.createSession({ + onPermissionRequest: approveAll, + enableConfigDiscovery: false, + mcpServers: { + peer: { + type: "local", + command: process.execPath, + args: [TEST_MCP_SERVER], + tools: ["*"], + }, + }, + }); + try { + const connected = async (serverName: string) => { + await waitForCondition( + async () => + (await session.rpc.mcp.list()).servers.some( + (server) => + server.name === serverName && server.status === "connected" + ), + { timeoutMs: 30_000, timeoutMessage: `${serverName} did not connect` } + ).catch(async (error: unknown) => { + const current = (await session.rpc.mcp.list()).servers.map( + ({ name, status }) => ({ name, status }) + ); + const installed = (await writer.rpc.plugins.list()).plugins.filter( + (plugin) => plugin.name === BROWSER_NAME + ); + const discovered = ( + await owner.rpc.mcp.discover({ workingDirectory: workDir }) + ).servers.map(({ name, enabled }) => ({ name, enabled })); + throw new Error( + `${serverName} did not connect: ${JSON.stringify({ current, installed, discovered })}`, + { cause: error } + ); + }); + expect((await session.rpc.mcp.listTools({ serverName })).tools).toEqual( + expect.arrayContaining([expect.objectContaining({ name: "get_env" })]) + ); + }; + const absent = async (serverName: string) => { + expect( + (await session.rpc.mcp.list()).servers.some( + (server) => server.name === serverName + ) + ).toBe(false); + }; + await connected("peer"); + await connected(BROWSER_NAME); + + const installed = await writer.rpc.plugins.install({ source: shadow }); + expect(installed.plugin.name).toBe(BROWSER_NAME); + expect(installed.plugin.enabled).toBe(true); + await session.rpc.mcp.reload(); + await connected(SHADOW_SERVER); + await absent(BROWSER_NAME); + + await writer.rpc.plugins.disable({ names: [BROWSER_NAME] }); + await session.rpc.mcp.reload(); + await absent(SHADOW_SERVER); + await absent(BROWSER_NAME); + + await writer.rpc.plugins.uninstall({ name: BROWSER_NAME }); + expect( + JSON.parse(readFileSync(join(env.COPILOT_HOME, "settings.json"), "utf8")) + .enabledPlugins[BROWSER_NAME] + ).toBe(true); + await session.rpc.mcp.reload(); + await connected(BROWSER_NAME); + await absent(SHADOW_SERVER); + } finally { + await session.disconnect(); + } + } + ); +}); diff --git a/nodejs/test/e2e/commands.e2e.test.ts b/nodejs/test/e2e/commands.e2e.test.ts index 0a4327370e..9cd17759ff 100644 --- a/nodejs/test/e2e/commands.e2e.test.ts +++ b/nodejs/test/e2e/commands.e2e.test.ts @@ -2,7 +2,10 @@ * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ -import { afterAll, describe, expect, it } from "vitest"; +import { randomUUID } from "node:crypto"; +import { writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import { afterAll, describe, expect, it, onTestFinished } from "vitest"; import { CopilotClient, approveAll, RuntimeConnection } from "../../src/index.js"; import type { CommandContext, SessionEvent } from "../../src/index.js"; import { createSdkTestContext } from "./harness/sdkTestContext.js"; @@ -273,6 +276,82 @@ describe("Commands", async () => { }); }); +describe("Usage command after restart", async () => { + // Custom-provider sessions bypass the server's Copilot billing account. + const { createClient, openAiEndpoint, env } = await createSdkTestContext({ + backend: "capi", + }); + + it.each([ + { + status: "partial", + totalNanoAiu: 23_275_500_000, + expected: "Requests: 23.28 AI Credits reported (partial)", + }, + { + status: "unavailable", + totalNanoAiu: 0, + expected: "Requests: unavailable AI Credits", + }, + ])("reports $status checkpoint AI credits", async ({ status, totalNanoAiu, expected }) => { + const token = `usage-command-checkpoint-token-${randomUUID()}`; + await openAiEndpoint.setCopilotUserByToken(token, { + login: "usage-command-user", + copilot_plan: "individual_pro", + token_based_billing: true, + endpoints: { api: openAiEndpoint.url }, + }); + const originalClient = createClient({ + connection: RuntimeConnection.forStdio(), + gitHubToken: token, + }); + onTestFinished(async () => { + expect(await originalClient.stop()).toHaveLength(0); + }); + const original = await originalClient.createSession({ + onPermissionRequest: approveAll, + }); + await original.rpc.name.set({ name: "Usage checkpoint fixture" }); + const events = await original.getEvents(); + const checkpoint: SessionEvent = { + type: "session.usage_checkpoint", + id: randomUUID(), + timestamp: new Date().toISOString(), + parentId: events.at(-1)?.id ?? null, + data: { totalNanoAiu, totalPremiumRequests: 1 }, + }; + await originalClient.rpc.sessions.save({ sessionId: original.sessionId }); + await originalClient.rpc.sessions.close({ sessionId: original.sessionId }); + expect(await originalClient.stop()).toHaveLength(0); + + // Seed durable accounting only after the session writer has stopped. + await writeFile( + join(env.COPILOT_HOME, "session-state", original.sessionId, "events.jsonl"), + `${[...events, checkpoint].map((event) => JSON.stringify(event)).join("\n")}\n` + ); + const resumedClient = createClient({ + connection: RuntimeConnection.forStdio(), + gitHubToken: token, + }); + onTestFinished(async () => { + expect(await resumedClient.stop()).toHaveLength(0); + }); + const resumed = await resumedClient.resumeSession(original.sessionId, { + onPermissionRequest: approveAll, + }); + expect(await resumed.rpc.usage.getMetrics()).toMatchObject({ + totalNanoAiu: checkpoint.data.totalNanoAiu, + totalPremiumRequestCost: checkpoint.data.totalPremiumRequests, + aiCreditsStatus: status, + }); + const result = await resumed.rpc.commands.invoke({ name: "usage" }); + expect(result.kind).toBe("text"); + if (result.kind !== "text") throw new Error(`Unexpected usage result: ${result.kind}`); + expect(result.text).toContain(expected); + expect(result.text).not.toContain("1 AI Units"); + }); +}); + function isCommand(command: { name: string; kind: string }, name: string, kind: string): boolean { return command.name.toLowerCase() === name.toLowerCase() && command.kind === kind; } diff --git a/nodejs/test/e2e/compaction.e2e.test.ts b/nodejs/test/e2e/compaction.e2e.test.ts index 4c8141b5e6..f42e515cdf 100644 --- a/nodejs/test/e2e/compaction.e2e.test.ts +++ b/nodejs/test/e2e/compaction.e2e.test.ts @@ -2,7 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ -import { describe, expect, it } from "vitest"; +import { describe, expect, it, onTestFailed } from "vitest"; import { existsSync } from "node:fs"; import * as fs from "node:fs/promises"; import { join } from "node:path"; @@ -15,11 +15,37 @@ import { } from "../../src/index.js"; import { createSdkTestContext, isCI } from "./harness/sdkTestContext.js"; import { testBackend } from "./harness/testBackend.js"; +import { withTestCleanup } from "../helpers/withTestCleanup.js"; const compactionTimeoutMs = 60_000; +interface CacheWireBlock { + text?: string; + cache_control?: unknown; + copilot_cache_control?: unknown; + output_config?: { effort?: string }; +} + +interface CacheWireRequest extends CacheWireBlock { + model: string; + max_tokens: number; + stream?: boolean; + system?: CacheWireBlock[]; + tools?: CacheWireBlock[]; + tool_choice?: unknown; + messages: (CacheWireBlock & { role: string; content: string | CacheWireBlock[] })[]; +} + class RefusalBudgetReplayHandler extends CopilotRequestHandler { - readonly requests: { model: string; max_tokens: number }[] = []; + readonly requests: CacheWireRequest[] = []; + readonly requestUrls: string[] = []; + + constructor( + private readonly endpoint: "/v1/messages" | "/chat/completions" = "/v1/messages", + private readonly fusionCritic = false + ) { + super(); + } protected override async sendRequest(request: Request): Promise { const path = new URL(request.url).pathname; @@ -28,6 +54,7 @@ class RefusalBudgetReplayHandler extends CopilotRequestHandler { data: [ ["claude-sonnet-5.5", 250_000, 384_000, 128_000], ["claude-sonnet-5", 112_000, 128_000, 64_000], + ...(this.fusionCritic ? [["claude-opus-5", 112_000, 128_000, 64_000]] : []), ].map(([id, prompt, context, output]) => ({ id, name: id, @@ -35,7 +62,21 @@ class RefusalBudgetReplayHandler extends CopilotRequestHandler { vendor: "Anthropic", version: "1", model_picker_enabled: true, - supported_endpoints: ["/v1/messages"], + policy: { state: "enabled" }, + supported_endpoints: [this.endpoint], + // Synthetic prices exercise TTL eligibility, not measured savings. + billing: { + token_prices: { + batch_size: 1_000_000, + default: { + input_price: 3, + output_price: 15, + cache_read_price: 0.3, + cache_write_price: 1, + cache_write_1h_price: 2, + }, + }, + }, capabilities: { type: "chat", family: id, @@ -50,17 +91,75 @@ class RefusalBudgetReplayHandler extends CopilotRequestHandler { })), }); } + if (this.fusionCritic && path === "/model/fusion") { + return Response.json({ + fusion_mode: "hydrafusion-max", + fusion_pattern: "critique", + plan_version: "1", + steps: [ + { role: "draft", model_id: "claude-opus-5" }, + { role: "critic", model_id: "claude-opus-5" }, + { role: "revision", model_id: "claude-opus-5" }, + ], + session: { token: "jwt.fake.cache-wire-fusion", expires_at: 0 }, + }); + } if (path.endsWith("/models/session")) return Response.json({}); if (path.includes("/policy")) return Response.json({ state: "enabled" }); - expect(path).toBe("/v1/messages"); - const body = (await request.json()) as { - model: string; - max_tokens: number; - stream?: boolean; - }; + expect(path).toBe(this.endpoint); + const body = (await request.json()) as CacheWireRequest; this.requests.push(body); + this.requestUrls.push(request.url); const refuses = body.model === "claude-sonnet-5.5"; - const content = refuses ? "PRIMARY_REFUSAL" : "FALLBACK_BUDGET_OK"; + const content = refuses + ? "PRIMARY_REFUSAL" + : this.fusionCritic && !body.tools?.length + ? JSON.stringify({ assessment: "approve", feedback: "" }) + : "FALLBACK_BUDGET_OK"; + const compaction = + Boolean(body.tools?.length) && + (body.tool_choice === "none" || + (typeof body.tool_choice === "object" && + body.tool_choice !== null && + "type" in body.tool_choice && + body.tool_choice.type === "none")); + // Model a warm tools/system prefix; this is accounting coverage, not a measured hit. + const cacheReadTokens = compaction ? 50 : 0; + if (this.endpoint === "/chat/completions") { + const usage = { + prompt_tokens: 100 + cacheReadTokens, + completion_tokens: 10, + total_tokens: 110 + cacheReadTokens, + prompt_tokens_details: { + cached_tokens: cacheReadTokens, + cache_creation_tokens: 0, + }, + }; + const choice = { + index: 0, + message: { role: "assistant", content }, + finish_reason: "stop", + }; + if (!body.stream) { + return Response.json({ + id: "cache-wire-replay", + object: "chat.completion", + model: body.model, + choices: [choice], + usage, + }); + } + const chunk = { + id: "cache-wire-replay", + object: "chat.completion.chunk", + model: body.model, + choices: [{ index: 0, delta: { content }, finish_reason: "stop" }], + usage, + }; + return new Response(`data: ${JSON.stringify(chunk)}\n\ndata: [DONE]\n\n`, { + headers: { "content-type": "text/event-stream" }, + }); + } const message = { id: "refusal-budget-replay", type: "message", @@ -69,7 +168,12 @@ class RefusalBudgetReplayHandler extends CopilotRequestHandler { content: [{ type: "text", text: content }], stop_reason: refuses ? "refusal" : "end_turn", stop_sequence: null, - usage: { input_tokens: 100, output_tokens: 10 }, + usage: { + input_tokens: 100, + output_tokens: 10, + cache_read_input_tokens: cacheReadTokens, + cache_creation_input_tokens: 0, + }, }; if (!body.stream) return Response.json(message); const events = [ @@ -104,6 +208,421 @@ class RefusalBudgetReplayHandler extends CopilotRequestHandler { } } +function expectNoConversationCacheMarkers(request: CacheWireRequest): void { + expect(request.cache_control).toBeUndefined(); + expect(Boolean(request.copilot_cache_control)).toBe(false); + for (const message of request.messages.filter(({ role }) => role !== "system")) { + expect(Boolean(message.copilot_cache_control)).toBe(false); + if (typeof message.content !== "string") { + for (const block of message.content) { + expect(block.cache_control).toBeUndefined(); + } + } + } +} + +function systemCacheMarkers(request: CacheWireRequest): unknown[] { + return [ + ...(request.system ?? []).map(({ cache_control }) => cache_control), + ...request.messages + .filter(({ role }) => role === "system") + .map(({ copilot_cache_control }) => copilot_cache_control), + ].filter(Boolean); +} + +for (const oneHour of [false, true]) { + describe(`Disposable Anthropic prompt caching (one-hour ${oneHour})`, async () => { + const { createClient } = await createSdkTestContext({ + replayOnly: true, + logLevel: "debug", + }); + + it.each(["CAPI Messages", "CAPI Chat", "BYOK Messages"] as const)( + "keeps reusable prefixes but not disposable writes on %s", + async (route) => { + const handler = new RefusalBudgetReplayHandler( + route === "CAPI Chat" ? "/chat/completions" : "/v1/messages" + ); + const cacheClient = createClient({ requestHandler: handler }); + let phase = "create session"; + const events: string[] = []; + onTestFailed(() => { + console.error( + `[cache-wire-progress] ${JSON.stringify({ + phase, + events: events.slice(-20), + requests: handler.requests.map((request, index) => ({ + endpoint: new URL(handler.requestUrls[index]!).pathname, + model: request.model, + stream: request.stream, + messages: request.messages.length, + hasTools: Boolean(request.tools?.length), + toolChoice: request.tool_choice, + })), + })}` + ); + }); + try { + const session = await cacheClient.createSession({ + onPermissionRequest: approveAll, + model: "claude-sonnet-5", + contextTier: "default", + featureFlags: { ANTHROPIC_1_HOUR_PROMPT_CACHE: oneHour }, + expAssignments: { + Features: ["copilot_cli_anthropic_1_hour_prompt_cache"], + Flights: { + copilot_cli_anthropic_1_hour_prompt_cache: String(oneHour), + }, + Configs: [ + { + Id: "default", + Parameters: { + copilot_cli_anthropic_1_hour_prompt_cache: oneHour, + }, + }, + ], + AssignmentContext: `cache-wire-one-hour-${oneHour}`, + }, + ...(route === "BYOK Messages" + ? { + provider: { + type: "anthropic" as const, + baseUrl: "https://cache-wire-replay.invalid", + apiKey: "fake-cache-wire-key", + modelId: "claude-sonnet-5", + }, + } + : { providers: [] }), + systemMessage: { + mode: "replace", + content: "Reusable agent policy. ".repeat(4_096), + }, + streaming: false, + infiniteSessions: { enabled: false }, + }); + session.on((event) => events.push(event.type)); + phase = "initial turn"; + expect( + ( + await session.sendAndWait({ + prompt: "Initialize the reusable conversation.", + }) + )?.data.content + ).toBe("FALLBACK_BUDGET_OK"); + const initial = handler.requests.at(-1)!; + expect( + new URL(handler.requestUrls.at(-1)!).hostname === + "cache-wire-replay.invalid" + ).toBe(route === "BYOK Messages"); + const marker = { + type: "ephemeral", + ...(oneHour && route !== "BYOK Messages" ? { ttl: "1h" } : {}), + }; + expect(systemCacheMarkers(initial)).toContainEqual(marker); + expect( + initial.tools?.some((tool) => + Boolean(tool.cache_control ?? tool.copilot_cache_control) + ) + ).toBe(true); + + const beforeSampling = handler.requests.length; + phase = "sampling and concurrent turn"; + const [sampling, concurrentTurn] = await Promise.all([ + session.rpc.mcp.executeSampling({ + requestId: "cache-wire-sampling", + serverName: "cache-wire-server", + mcpRequestId: "cache-wire-mcp", + request: { + systemPrompt: "Disposable caller-owned policy. ".repeat(4_096), + messages: [ + { + role: "user", + content: { + type: "text", + text: "Compute a sampling result.", + }, + }, + ], + maxTokens: 4096, + }, + }), + session.sendAndWait({ + prompt: "Continue while sampling runs independently.", + }), + ]); + expect(concurrentTurn?.data.content).toBe("FALLBACK_BUDGET_OK"); + expect(sampling).toMatchObject({ + action: "success", + result: { content: { type: "text", text: "FALLBACK_BUDGET_OK" } }, + }); + const concurrentRequests = handler.requests.slice(beforeSampling); + expect(concurrentRequests).toHaveLength(2); + const sampled = concurrentRequests.find((request) => !request.tools?.length)!; + const continued = concurrentRequests.find((request) => + Boolean(request.tools?.length) + )!; + expect(systemCacheMarkers(sampled)).toEqual([]); + expectNoConversationCacheMarkers(sampled); + expect(systemCacheMarkers(continued)).toContainEqual(marker); + + phase = "handoff summary"; + expect(await session.rpc.history.summarizeForHandoff()).toMatchObject({ + summary: "FALLBACK_BUDGET_OK", + }); + expect(systemCacheMarkers(handler.requests.at(-1)!)).toEqual([]); + expectNoConversationCacheMarkers(handler.requests.at(-1)!); + + phase = "manual compaction"; + const completed = getNextSessionEvent( + session, + "session.compaction_complete", + "manual cache compaction" + ); + const [compacted, completionEvent] = await Promise.all([ + session.rpc.history.compact({ trigger: "manual" }), + completed, + ]); + expect(compacted).toMatchObject({ + success: true, + summaryContent: "FALLBACK_BUDGET_OK", + }); + expect(completionEvent.data.compactionTokensUsed).toMatchObject({ + inputTokens: 150, + outputTokens: 10, + cacheReadTokens: 50, + cacheWriteTokens: 0, + }); + const compacting = handler.requests.at(-1)!; + expect(systemCacheMarkers(compacting)).toEqual(systemCacheMarkers(initial)); + expect(compacting.tools).toEqual(initial.tools); + expect(compacting.tool_choice).toEqual( + route === "CAPI Chat" ? "none" : { type: "none" } + ); + expectNoConversationCacheMarkers(compacting); + + phase = "post-compaction turn"; + expect( + (await session.sendAndWait({ prompt: "Continue after compaction." }))?.data + .content + ).toBe("FALLBACK_BUDGET_OK"); + expect(systemCacheMarkers(handler.requests.at(-1)!)).toContainEqual(marker); + phase = "disconnect session"; + await session.disconnect(); + } finally { + phase = `stop client (after ${phase})`; + await cacheClient.stop(); + } + } + ); + }); +} + +describe("Disposable Anthropic prompt caching during automatic compaction", async () => { + const { createClient } = await createSdkTestContext({ replayOnly: true }); + it.each(["CAPI Messages", "CAPI Chat", "BYOK Messages"] as const)( + "avoids discarded transcript writes during automatic compaction on %s", + async (route) => { + const handler = new RefusalBudgetReplayHandler( + route === "CAPI Chat" ? "/chat/completions" : "/v1/messages" + ); + const cacheClient = createClient({ requestHandler: handler }); + try { + const session = await cacheClient.createSession({ + onPermissionRequest: approveAll, + model: "claude-sonnet-5", + ...(route === "BYOK Messages" + ? { + provider: { + type: "anthropic" as const, + baseUrl: "https://cache-wire-replay.invalid", + apiKey: "fake-cache-wire-key", + modelId: "claude-sonnet-5", + }, + } + : { providers: [] }), + systemMessage: { + mode: "replace", + content: "Reusable automatic policy. ".repeat(600), + }, + streaming: false, + infiniteSessions: { + enabled: true, + backgroundCompactionThreshold: 0.005, + bufferExhaustionThreshold: 0.01, + }, + }); + const completed = getNextSessionEvent( + session, + "session.compaction_complete", + "automatic cache compaction" + ); + const [, completionEvent] = await Promise.all([ + (async () => { + expect( + ( + await session.sendAndWait({ + prompt: "Start the automatic compaction scenario.", + }) + )?.data.content + ).toBe("FALLBACK_BUDGET_OK"); + expect( + ( + await session.sendAndWait({ + prompt: "Continue the automatic compaction scenario.", + }) + )?.data.content + ).toBe("FALLBACK_BUDGET_OK"); + })(), + completed, + ]); + expect(completionEvent.data).toMatchObject({ + success: true, + summaryContent: "FALLBACK_BUDGET_OK", + compactionTokensUsed: { + inputTokens: 150, + outputTokens: 10, + cacheReadTokens: 50, + cacheWriteTokens: 0, + }, + }); + const compactions = handler.requests.filter(({ tool_choice }) => + route === "CAPI Chat" + ? tool_choice === "none" + : typeof tool_choice === "object" && + tool_choice !== null && + "type" in tool_choice && + tool_choice.type === "none" + ); + expect(compactions.length).toBeGreaterThan(0); + for (const request of compactions) { + expect(systemCacheMarkers(request)).not.toEqual([]); + expect(request.tools).toEqual(handler.requests[0].tools); + expectNoConversationCacheMarkers(request); + } + await session.disconnect(); + } finally { + await cacheClient.stop(); + } + } + ); +}); + +describe("Disposable Anthropic prompt caching during refusal fallback", async () => { + const { createClient } = await createSdkTestContext({ + replayOnly: true, + copilotClientOptions: { + env: { + ANTHROPIC_REFUSAL_FALLBACK: "true", + COPILOT_EXP_COPILOT_CLI_ANTHROPIC_REFUSAL_FALLBACK: "true", + }, + }, + }); + + it("preserves single-use cache intent across refusal fallback", async () => { + const handler = new RefusalBudgetReplayHandler(); + const cacheClient = createClient({ requestHandler: handler }); + try { + const session = await cacheClient.createSession({ + onPermissionRequest: approveAll, + model: "claude-sonnet-5.5", + providers: [], + infiniteSessions: { enabled: false }, + }); + const sampled = await session.rpc.mcp.executeSampling({ + requestId: "fallback-cache-sampling", + serverName: "cache-wire-server", + mcpRequestId: "fallback-cache-mcp", + request: { + systemPrompt: "Disposable fallback policy. ".repeat(4_096), + messages: [ + { + role: "user", + content: { type: "text", text: "Compute a sampling result." }, + }, + ], + maxTokens: 4096, + }, + }); + expect(sampled).toMatchObject({ + action: "success", + result: { content: { text: "FALLBACK_BUDGET_OK" } }, + }); + expect(handler.requests.map(({ model }) => model)).toEqual([ + "claude-sonnet-5.5", + "claude-sonnet-5", + ]); + for (const request of handler.requests) { + expect(systemCacheMarkers(request)).toEqual([]); + expectNoConversationCacheMarkers(request); + } + await session.disconnect(); + } finally { + await cacheClient.stop(); + } + }); +}); + +describe("Disposable Anthropic prompt caching during Fusion review", async () => { + const { createClient } = await createSdkTestContext({ replayOnly: true }); + it("avoids tool-less critic writes without disabling the solver cache", async () => { + const handler = new RefusalBudgetReplayHandler("/v1/messages", true); + const cacheClient = createClient({ requestHandler: handler }); + try { + const session = await cacheClient.createSession({ + onPermissionRequest: approveAll, + model: "hydrafusion-max", + providers: [], + enableExperimentalMode: true, + featureFlags: { + HYDRAFUSION: true, + HYDRAFUSION_ROLLOUT: true, + HYDRAFUSION_PLAN_V2: true, + }, + expAssignments: { + Features: [], + Flights: {}, + Configs: [], + AssignmentContext: "cache-wire-fusion-review", + }, + infiniteSessions: { enabled: false }, + }); + const events: SessionEvent[] = []; + expect((await session.rpc.model.getCurrent()).modelId).toBe("hydrafusion-max"); + const unsubscribe = session.on((event) => events.push(event)); + try { + expect( + ( + await session.sendAndWait({ + prompt: "Answer the cache policy review scenario without using tools.", + }) + )?.data.content + ).toBe("FALLBACK_BUDGET_OK"); + expect( + events.filter((event) => event.type === "session.fusion_route_failed") + ).toEqual([]); + expect( + events + .filter((event) => event.type === "assistant.fusion_phase_completed") + .map((event) => event.data.phaseKind) + ).toEqual(["draft", "critic"]); + expect(handler.requests).toHaveLength(2); + expect(systemCacheMarkers(handler.requests[0])).not.toEqual([]); + expect(handler.requests[0].tools?.length).toBeGreaterThan(0); + const critic = handler.requests[1]; + expect(critic.model).toBe("claude-opus-5"); + expect(critic.tools ?? []).toEqual([]); + expect(systemCacheMarkers(critic)).toEqual([]); + expectNoConversationCacheMarkers(critic); + } finally { + unsubscribe(); + await session.disconnect(); + } + } finally { + await cacheClient.stop(); + } + }); +}); + class BudgetReplayHandler extends CopilotRequestHandler { readonly requests: unknown[] = []; @@ -144,6 +663,300 @@ class BudgetReplayHandler extends CopilotRequestHandler { } } +interface CompactionEffortRequest { + model: string; + stream?: boolean; + reasoning?: { effort?: string }; + input: Array<{ type?: string; role?: string; reasoning?: { effort?: string } }>; +} + +class CompactionEffortReplayHandler extends CopilotRequestHandler { + readonly requests: CompactionEffortRequest[] = []; + summaryGate?: { + entered: ReturnType>; + release: ReturnType>; + }; + + protected override async sendRequest(request: Request): Promise { + expect(new URL(request.url).pathname).toBe("/responses"); + const body = (await request.json()) as CompactionEffortRequest; + this.requests.push(body); + expect(body.stream).not.toBe(true); + const incompleteSummary = this.requests.length === 2 && this.summaryGate; + if (incompleteSummary) { + incompleteSummary.entered.resolve(); + await incompleteSummary.release.promise; + } + return Response.json({ + id: "compaction-effort-response", + object: "response", + model: body.model, + status: incompleteSummary ? "incomplete" : "completed", + ...(incompleteSummary ? { incomplete_details: { reason: "max_output_tokens" } } : {}), + output: [ + { + id: "compaction-effort-message", + type: "message", + role: "assistant", + status: "completed", + content: [ + { + type: "output_text", + text: incompleteSummary ? "SUMMARY_PART_ONE" : "COMPACTION_EFFORT_OK", + }, + ], + }, + ], + usage: { input_tokens: 100, output_tokens: 10, total_tokens: 110 }, + }); + } +} + +describe("Compaction reasoning effort", async () => { + const { createClient } = await createSdkTestContext({ replayOnly: true }); + + it("reduces only the OpenAI summary request and retains the session effort", async () => { + const handler = new CompactionEffortReplayHandler(); + const effortClient = createClient({ requestHandler: handler }); + try { + await using session = await effortClient.createSession({ + onPermissionRequest: approveAll, + model: "gpt-6-sol", + reasoningEffort: "max", + provider: { + type: "openai", + wireApi: "responses", + baseUrl: "https://compaction-effort.invalid", + apiKey: "fake-byok-credential-for-e2e-tests", + modelId: "gpt-6-sol", + }, + streaming: false, + featureFlags: { COMPACTION_MEDIUM_REASONING_EFFORT: true }, + expAssignments: { + Features: [], + Flights: {}, + Configs: [], + AssignmentContext: "compaction-medium-effort", + }, + infiniteSessions: { enabled: false }, + }); + expect( + (await session.sendAndWait({ prompt: "Initialize the effort scenario." }))?.data + .content + ).toBe("COMPACTION_EFFORT_OK"); + expect(handler.requests).toHaveLength(1); + expect(handler.requests[0].reasoning?.effort).toBe("max"); + + const compacted = await session.rpc.history.compact({ trigger: "manual" }); + expect(compacted).toMatchObject({ + success: true, + summaryContent: "COMPACTION_EFFORT_OK", + }); + expect(handler.requests).toHaveLength(2); + const summaryRequest = handler.requests[1]; + expect(summaryRequest.reasoning?.effort).toBe("max"); + expect( + summaryRequest.input.filter((item) => item.type === "configuration_update") + ).toEqual([{ type: "configuration_update", reasoning: { effort: "medium" } }]); + expect(summaryRequest.input.at(-2)?.reasoning?.effort).toBe("medium"); + expect((await session.rpc.model.getCurrent()).reasoningEffort).toBe("max"); + + expect( + (await session.sendAndWait({ prompt: "Continue after the summary." }))?.data.content + ).toBe("COMPACTION_EFFORT_OK"); + expect(handler.requests).toHaveLength(3); + const nextRequest = handler.requests[2]; + expect(nextRequest.reasoning?.effort).toBe("max"); + expect( + nextRequest.input.filter((item) => item.type === "configuration_update") + ).toEqual([]); + } finally { + await effortClient.stop(); + } + }); + + it("preserves Anthropic compaction effort even when the reduction is enabled", async () => { + const handler = new RefusalBudgetReplayHandler(); + const effortClient = createClient({ requestHandler: handler }); + try { + await using session = await effortClient.createSession({ + onPermissionRequest: approveAll, + model: "claude-opus-5.5", + reasoningEffort: "max", + provider: { + type: "anthropic", + baseUrl: "https://compaction-effort.invalid", + bearerToken: "fake-byok-credential-for-e2e-tests", + modelId: "claude-opus-5.5", + }, + featureFlags: { COMPACTION_MEDIUM_REASONING_EFFORT: true }, + expAssignments: { + Features: [], + Flights: {}, + Configs: [], + AssignmentContext: "compaction-medium-effort", + }, + infiniteSessions: { enabled: false }, + }); + expect( + (await session.sendAndWait({ prompt: "Initialize the effort scenario." }))?.data + .content + ).toBe("FALLBACK_BUDGET_OK"); + expect(handler.requests).toHaveLength(1); + expect(handler.requests[0].output_config?.effort).toBe("max"); + + const compacted = await session.rpc.history.compact({ trigger: "manual" }); + expect(compacted).toMatchObject({ + success: true, + summaryContent: "FALLBACK_BUDGET_OK", + }); + expect(handler.requests).toHaveLength(2); + const summaryRequest = handler.requests[1]; + expect(summaryRequest.output_config?.effort).toBe("max"); + expect( + summaryRequest.messages.flatMap((message) => + message.output_config?.effort ? [message.output_config.effort] : [] + ) + ).toEqual([]); + expect((await session.rpc.model.getCurrent()).reasoningEffort).toBe("max"); + + expect( + (await session.sendAndWait({ prompt: "Continue after the summary." }))?.data.content + ).toBe("FALLBACK_BUDGET_OK"); + expect(handler.requests).toHaveLength(3); + const nextRequest = handler.requests[2]; + expect(nextRequest.output_config?.effort).toBe("max"); + expect(nextRequest.messages.filter((message) => message.output_config)).toEqual([]); + } finally { + await effortClient.stop(); + } + }); + + it("isolates compaction effort from concurrent sampling and selected effort changes", async ({ + expect, + onTestFinished, + }) => { + const handler = new CompactionEffortReplayHandler(); + const gate = { + entered: Promise.withResolvers(), + release: Promise.withResolvers(), + }; + handler.summaryGate = gate; + const effortClient = createClient({ requestHandler: handler }); + const stopClient = async () => { + gate.release.resolve(); + const errors = await effortClient.stop(); + if (errors.length) { + throw new AggregateError(errors, "Failed to stop compaction overlap client"); + } + }; + onTestFinished(stopClient); + await withTestCleanup(async () => { + const session = await effortClient.createSession({ + onPermissionRequest: approveAll, + model: "gpt-6-sol", + reasoningEffort: "max", + provider: { + type: "openai", + wireApi: "responses", + baseUrl: "https://compaction-effort.invalid", + apiKey: "fake-byok-credential-for-e2e-tests", + modelId: "gpt-6-sol", + }, + streaming: false, + featureFlags: { COMPACTION_MEDIUM_REASONING_EFFORT: true }, + expAssignments: { + Features: [], + Flights: {}, + Configs: [], + AssignmentContext: "compaction-overlap", + }, + infiniteSessions: { enabled: false }, + }); + expect( + (await session.sendAndWait({ prompt: "Initialize the overlap scenario." }))?.data + .content + ).toBe("COMPACTION_EFFORT_OK"); + const compaction = session.rpc.history.compact({ trigger: "manual" }); + await withTestCleanup( + async () => { + await Promise.race([ + gate.entered.promise, + compaction.then((result) => { + throw new Error( + `Compaction completed before its summary gate: ${JSON.stringify(result)}` + ); + }), + ]); + expect(handler.requests).toHaveLength(2); + expect(handler.requests[1].input.at(-2)?.reasoning?.effort).toBe("medium"); + expect((await session.rpc.model.getCurrent()).reasoningEffort).toBe("max"); + + expect( + await session.rpc.mcp.executeSampling({ + requestId: "compaction-overlap-sampling", + serverName: "compaction-overlap-server", + mcpRequestId: "compaction-overlap-mcp", + request: { + messages: [ + { + role: "user", + content: { + type: "text", + text: "Compute a sampling result.", + }, + }, + ], + maxTokens: 4096, + }, + }) + ).toMatchObject({ + action: "success", + result: { content: { type: "text", text: "COMPACTION_EFFORT_OK" } }, + }); + expect(handler.requests).toHaveLength(3); + expect(handler.requests[2].reasoning?.effort).toBe("max"); + expect( + handler.requests[2].input.filter( + (item) => item.type === "configuration_update" + ) + ).toEqual([]); + + await session.rpc.model.setReasoningEffort({ reasoningEffort: "high" }); + expect((await session.rpc.model.getCurrent()).reasoningEffort).toBe("high"); + }, + () => gate.release.resolve(), + async () => { + expect(await compaction).toMatchObject({ + success: true, + summaryContent: expect.stringContaining("COMPACTION_EFFORT_OK"), + }); + } + ); + expect(handler.requests).toHaveLength(4); + const continuation = handler.requests[3]; + expect(continuation.reasoning?.effort).toBe("max"); + expect( + continuation.input.filter((item) => item.type === "configuration_update") + ).toEqual([{ type: "configuration_update", reasoning: { effort: "medium" } }]); + expect((await session.rpc.model.getCurrent()).reasoningEffort).toBe("high"); + expect( + (await session.sendAndWait({ prompt: "Continue after the held summary." }))?.data + .content + ).toBe("COMPACTION_EFFORT_OK"); + expect(handler.requests).toHaveLength(5); + // The cache baseline stays pinned; the durable user boundary selects high. + expect(handler.requests[4].reasoning?.effort).toBe("max"); + expect( + handler.requests[4].input.filter((item) => item.type === "configuration_update") + ).toEqual([{ type: "configuration_update", reasoning: { effort: "high" } }]); + expect(handler.requests[4].input.at(-2)?.reasoning?.effort).toBe("high"); + expect((await session.rpc.model.getCurrent()).reasoningEffort).toBe("high"); + await session.disconnect(); + }, stopClient); + }); +}); + describe("Refusal fallback output budget", async () => { const { createClient } = await createSdkTestContext(); diff --git a/nodejs/test/e2e/computer_use_oauth.e2e.test.ts b/nodejs/test/e2e/computer_use_oauth.e2e.test.ts new file mode 100644 index 0000000000..76bdd90390 --- /dev/null +++ b/nodejs/test/e2e/computer_use_oauth.e2e.test.ts @@ -0,0 +1,347 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { execFileSync } from "node:child_process"; +import { mkdir, mkdtemp, readFile, realpath, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { describe, test, type TestContext } from "vitest"; +import { approveAll, CopilotClient, RuntimeConnection } from "../../src/index.js"; +import { startComputerUseOAuthServer } from "../../../test/harness/test-mcp-computer-use-oauth-server.mjs"; +import { createSdkTestContext, DEFAULT_GITHUB_TOKEN } from "./harness/sdkTestContext.js"; + +const TEST_MCP_SERVER = fileURLToPath( + new URL("../../../test/harness/test-mcp-server.mjs", import.meta.url) +); +const SERVER_NAME = "unrelated-oauth"; + +describe("Computer Use OAuth isolation", async () => { + const { env } = await createSdkTestContext(); + + async function createFixture(context: TestContext, preference?: boolean, startup = false) { + // The shared harness clears workDir before this fixture's onTestFinished cleanup. + const directory = await realpath(await mkdtemp(join(tmpdir(), "computer-use-oauth-"))); + const home = join(directory, "home"); + const plugin = join(directory, "computer-use"); + let client: CopilotClient | undefined; + const startingOAuthServer = startComputerUseOAuthServer({ + deferRegistration: !startup, + deferInitialChallenge: startup, + deferAuthorizationMetadata: startup, + preconfiguredClientId: startup ? "app-client" : "", + }); + context.onTestFinished(async () => { + const errors: unknown[] = []; + const [serverStartup] = await Promise.allSettled([startingOAuthServer]); + if (serverStartup.status === "fulfilled") { + serverStartup.value.releaseRegistration(); + serverStartup.value.releaseInitialChallenge(); + serverStartup.value.releaseAuthorizationMetadata(); + } + if (client) { + try { + await readFile(join(plugin, ".plugin", "plugin.json"), "utf8"); + } catch (error) { + errors.push( + new Error( + "Computer Use OAuth fixture was removed before its client stopped", + { + cause: error, + } + ) + ); + } + try { + errors.push(...(await client.stop())); + } catch (error) { + errors.push(error); + } + } + if (serverStartup.status === "fulfilled") { + try { + await serverStartup.value.close(); + } catch (error) { + errors.push(error); + } + } + try { + await rm(directory, { + recursive: true, + force: true, + maxRetries: 10, + retryDelay: 100, + }); + } catch (error) { + errors.push(error); + } + if (errors.length) + throw new AggregateError(errors, "Computer Use OAuth cleanup failed"); + }); + const oauthServer = await startingOAuthServer; + context.signal.throwIfAborted(); + await mkdir(home); + await mkdir(join(plugin, ".plugin"), { recursive: true }); + execFileSync("git", ["init", "--quiet", directory], { windowsHide: true }); + if (preference !== undefined) { + await writeFile( + join(home, "settings.json"), + JSON.stringify({ + enabledPlugins: { "computer-use": preference }, + }) + ); + } + await writeFile( + join(plugin, ".plugin", "plugin.json"), + JSON.stringify({ + name: "computer-use", + version: "1.0.0", + mcpServers: "./.mcp.json", + }) + ); + await writeFile( + join(plugin, ".mcp.json"), + JSON.stringify({ + mcpServers: { + "computer-use": { + command: process.execPath, + args: [TEST_MCP_SERVER, "--server-name", "computer-use"], + cwd: dirname(TEST_MCP_SERVER), + tools: ["*"], + }, + }, + }) + ); + client = new CopilotClient({ + workingDirectory: directory, + env: { + ...env, + COPILOT_HOME: home, + GH_CONFIG_DIR: home, + XDG_CONFIG_HOME: home, + XDG_STATE_HOME: home, + COPILOT_DISABLE_KEYTAR: "1", + }, + gitHubToken: DEFAULT_GITHUB_TOKEN, + connection: RuntimeConnection.forStdio({ path: process.env.COPILOT_CLI_PATH }), + }); + await client.start(); + context.signal.throwIfAborted(); + await client.rpc.plugins.builtin.set({ paths: [plugin] }); + const session = await client.createSession({ + workingDirectory: directory, + onPermissionRequest: approveAll, + onMcpAuthRequest: async () => ({ kind: "cancelled" }), + mcpServers: { + [SERVER_NAME]: { + type: "http", + url: `${oauthServer.url}/mcp`, + tools: ["*"], + ...(startup ? { oauthClientId: "app-client", oauthPublicClient: true } : {}), + }, + }, + }); + return { client, session, oauthServer }; + } + + test.for([ + { preference: "unset", hosted: false }, + { preference: "disabled", hosted: false }, + { preference: "unset", hosted: true }, + { preference: "disabled", hosted: true }, + ] as const)( + "returns the initial OAuth URL across Computer Use toggles ($preference, hosted=$hosted)", + { timeout: 90_000 }, + async ({ preference, hosted }, context) => { + const { expect } = context; + const { client, session, oauthServer } = await createFixture( + context, + preference === "disabled" ? false : undefined + ); + const [initial] = await Promise.all([ + session.rpc.mcp.list(), + session.rpc.plugins.list(), + ]); + expect(initial.servers.map((server) => server.name)).not.toContain("computer-use"); + const login = session.rpc.mcp.oauth + .login({ + serverName: SERVER_NAME, + forceReauth: true, + ...(hosted ? { redirectUri: "https://agent.example.test/oauth/callback" } : {}), + }) + .then( + (result) => ({ result }), + (error: unknown) => ({ error }) + ); + // Registration is after lifetime capture but before the browser URL is returned. + await expect + .poll(() => oauthServer.requests.map((request) => request.path), { + timeout: 60_000, + }) + .toContain("/register"); + for (const enabled of [true, false, true]) { + if (enabled) await client.rpc.plugins.enable({ names: ["computer-use"] }); + else await client.rpc.plugins.disable({ names: ["computer-use"] }); + await expect + .poll( + async () => + (await session.rpc.mcp.list()).servers.find( + (candidate) => candidate.name === "computer-use" + )?.status, + { timeout: 60_000 } + ) + .toBe(enabled ? "connected" : undefined); + } + oauthServer.releaseRegistration(); + const outcome = await login; + if ("error" in outcome) throw outcome.error; + const result = outcome.result; + expect(result.authorizationUrl).toBeDefined(); + const authorization = new URL(result.authorizationUrl!); + expect(authorization.origin).toBe(oauthServer.url); + expect(authorization.searchParams.get("client_id")).toBe("registered-client"); + + await client.rpc.plugins.disable({ names: ["computer-use"] }); + await expect + .poll( + async () => (await session.rpc.mcp.list()).servers.map((server) => server.name), + { timeout: 60_000 } + ) + .not.toContain("computer-use"); + const callback = new URL(authorization.searchParams.get("redirect_uri")!); + callback.searchParams.set("code", "accepted-code"); + callback.searchParams.set("state", authorization.searchParams.get("state")!); + if (hosted) { + expect(result.authorizationId).toBe(authorization.searchParams.get("state")); + expect(callback.origin).toBe("https://agent.example.test"); + await session.rpc.mcp.oauth.complete({ + authorizationId: result.authorizationId!, + callbackUrl: callback.toString(), + }); + } else { + const response = await fetch(callback); + expect(response.ok).toBe(true); + } + await expect + .poll( + async () => + (await session.rpc.mcp.list()).servers.find( + (server) => server.name === SERVER_NAME + )?.status, + { timeout: 60_000 } + ) + .toBe("connected"); + expect( + (await session.rpc.mcp.listTools({ serverName: SERVER_NAME })).tools.map( + (tool) => tool.name + ) + ).toContain("whoami"); + const tokens = oauthServer.requests.filter((request) => request.path === "/token"); + expect(tokens).toHaveLength(1); + expect(new URLSearchParams(tokens[0].body).get("client_id")).toBe("registered-client"); + } + ); + + test.for(["unset", "disabled"] as const)( + "preserves preconfigured-client OAuth overlapping initial discovery (%s preference)", + { timeout: 90_000 }, + async (preference, context) => { + const { expect } = context; + const { client, session, oauthServer } = await createFixture( + context, + preference === "disabled" ? false : undefined, + true + ); + const discovery = session.rpc.mcp.list().then( + (result) => ({ result }), + (error: unknown) => ({ error }) + ); + const login = session.rpc.mcp.oauth.login({ serverName: SERVER_NAME }).then( + (result) => ({ result }), + (error: unknown) => ({ error }) + ); + await expect + .poll(() => oauthServer.requests.filter((request) => request.path === "/mcp"), { + timeout: 60_000, + }) + .not.toHaveLength(0); + await session.rpc.plugins.list(); + oauthServer.releaseInitialChallenge(); + await expect + .poll(() => oauthServer.requests.map((request) => request.path), { + timeout: 60_000, + }) + .toContain("/.well-known/oauth-authorization-server"); + const discovered = await discovery; + if ("error" in discovered) throw discovered.error; + expect(discovered.result.servers.map((server) => server.name)).not.toContain( + "computer-use" + ); + await client.rpc.plugins.enable({ names: ["computer-use"] }); + await expect + .poll( + async () => + (await session.rpc.mcp.list()).servers.find( + (server) => server.name === "computer-use" + )?.status, + { timeout: 60_000 } + ) + .toBe("connected"); + oauthServer.releaseAuthorizationMetadata(); + const outcome = await login; + if ("error" in outcome) throw outcome.error; + const authorization = new URL(outcome.result.authorizationUrl!); + expect(authorization.searchParams.get("client_id")).toBe("app-client"); + const callback = new URL(authorization.searchParams.get("redirect_uri")!); + callback.searchParams.set("code", "accepted-code"); + callback.searchParams.set("state", authorization.searchParams.get("state")!); + expect((await fetch(callback)).ok).toBe(true); + await expect + .poll( + async () => + (await session.rpc.mcp.list()).servers.find( + (server) => server.name === SERVER_NAME + )?.status, + { timeout: 60_000 } + ) + .toBe("connected"); + expect( + (await session.rpc.mcp.listTools({ serverName: SERVER_NAME })).tools.map( + (tool) => tool.name + ) + ).toContain("whoami"); + expect( + oauthServer.requests.filter((request) => request.path === "/register") + ).toHaveLength(0); + const tokens = oauthServer.requests.filter((request) => request.path === "/token"); + expect(tokens).toHaveLength(1); + expect(new URLSearchParams(tokens[0].body).get("client_id")).toBe("app-client"); + } + ); + + test("still cancels initial OAuth when its own server is reloaded", async (context) => { + const { expect } = context; + const { session, oauthServer } = await createFixture(context); + await session.rpc.mcp.list(); + const login = session.rpc.mcp.oauth + .login({ serverName: SERVER_NAME, forceReauth: true }) + .then( + (result) => ({ result }), + (error: unknown) => ({ error }) + ); + await expect + .poll(() => oauthServer.requests.map((request) => request.path), { timeout: 60_000 }) + .toContain("/register"); + await session.rpc.mcp.reload(); + oauthServer.releaseRegistration(); + expect(await login).toEqual({ + error: expect.objectContaining({ + message: expect.stringContaining( + "MCP OAuth was cancelled because its original requester or configuration changed." + ), + }), + }); + expect(oauthServer.requests.filter((request) => request.path === "/token")).toHaveLength(0); + }, 90_000); +}); diff --git a/nodejs/test/e2e/copilot_request_handler.e2e.test.ts b/nodejs/test/e2e/copilot_request_handler.e2e.test.ts index af79d84ae3..6faaf40a32 100644 --- a/nodejs/test/e2e/copilot_request_handler.e2e.test.ts +++ b/nodejs/test/e2e/copilot_request_handler.e2e.test.ts @@ -4,6 +4,8 @@ import { createServer, IncomingMessage, Server as HttpServer, ServerResponse } from "http"; import { AddressInfo } from "net"; +import { writeFile } from "node:fs/promises"; +import { join } from "node:path"; import { afterAll, describe, expect, it } from "vitest"; import { WebSocketServer } from "ws"; import { @@ -12,9 +14,11 @@ import { CopilotWebSocketForwarder, defineTool, type CopilotRequestContext, + type SessionConfig, } from "../../src/index.js"; import { createSdkTestContext } from "./harness/sdkTestContext.js"; import { waitForCondition } from "./harness/sdkTestHelper.js"; +import { mcpTestServerConfig } from "./harness/mcpTestServer.js"; const HTTP_TEXT = "OK from synthetic HTTP upstream."; const WS_TEXT = "OK from synthetic WS upstream."; @@ -36,11 +40,13 @@ async function startFakeUpstream(): Promise<{ wsRequestCount: () => number; wsBodies: () => Array>; httpBodies: () => Array>; + chatBodies: () => Array>; close: () => Promise; }> { let wsRequests = 0; const wsBodies: Array> = []; const httpBodies: Array> = []; + const chatBodies: Array> = []; const httpServer = createServer((req, res) => { const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`); @@ -72,6 +78,20 @@ async function startFakeUpstream(): Promise<{ }, }, }, + { + id: "gpt-5.5", + name: "GPT-5.5", + vendor: "OpenAI", + model_picker_enabled: true, + supported_endpoints: ["/chat/completions"], + capabilities: { + type: "chat", + family: "gpt-5.5", + tokenizer: "o200k_base", + limits: { max_context_window_tokens: 200000, max_output_tokens: 8192 }, + supports: { streaming: true, tool_calls: true }, + }, + }, { id: "gpt-6-sol", name: "GPT-6 Sol", @@ -108,12 +128,73 @@ async function startFakeUpstream(): Promise<{ sendJson(res, 200, { state: "enabled" }); return; } + if (url.pathname.endsWith("/chat/completions") && req.method === "POST") { + drainBody(req) + .then((raw) => { + const body = JSON.parse(raw.toString()) as Record; + chatBodies.push(body); + const serialized = JSON.stringify(body.messages); + const toolResult = (body.messages as Array<{ role: string }>).some( + (message) => message.role === "tool" + ); + const call = + serialized.includes("SDK_CHAT_RESTORED") && + !serialized.includes("SDK_CHAT_EXECUTED_RESULT"); + const removedCall = serialized.includes("SDK_CHAT_REMOVED") && !toolResult; + const message = + call || removedCall + ? { + role: "assistant", + content: null, + tool_calls: [ + { + index: 0, + id: "chat_call", + type: "function", + function: { name: "sdk_chat_removed", arguments: "{}" }, + }, + ], + } + : { + role: "assistant", + content: serialized.includes("SDK_CHAT_RESTORED") + ? "SDK_CHAT_RESTORED_OK" + : serialized.includes("SDK_CHAT_REMOVED") + ? "SDK_CHAT_REMOVED_OK" + : "SDK_CHAT_FIRST_OK", + }; + const finishReason = call || removedCall ? "tool_calls" : "stop"; + if (body.stream !== true) { + sendJson(res, 200, { + id: "chat_response", + choices: [{ index: 0, message, finish_reason: finishReason }], + usage: { prompt_tokens: 5, completion_tokens: 7 }, + }); + return; + } + res.writeHead(200, { "content-type": "text/event-stream" }); + res.write( + `data: ${JSON.stringify({ id: "chat_response", choices: [{ index: 0, delta: message, finish_reason: null }] })}\n\n` + ); + res.end( + `data: ${JSON.stringify({ id: "chat_response", choices: [{ index: 0, delta: {}, finish_reason: finishReason }] })}\n\ndata: [DONE]\n\n` + ); + }) + .catch((error: unknown) => { + sendJson(res, 500, { error: String(error) }); + }); + return; + } if (url.pathname.endsWith("/responses") && req.method === "POST") { drainBody(req) .then((raw) => { const body = JSON.parse(raw.toString()) as Record; httpBodies.push(body); - const events = buildResponsesEvents(HTTP_TEXT, "resp_stub_http"); + const events = toolUpdateEvents(body, "resp_stub_http", HTTP_TEXT); + if (events[0]?.type === "error") { + sendJson(res, 422, { error: events[0].error }); + return; + } if (body.stream !== true) { sendJson(res, 200, events.at(-1)!.response); return; @@ -143,69 +224,8 @@ async function startFakeUpstream(): Promise<{ wsRequests++; const body = JSON.parse(raw.toString()) as Record; wsBodies.push(body); - const input = JSON.stringify(body.input); const id = `resp_stub_ws_${wsRequests}`; - const positionedUpdate = - Array.isArray(body.input) && - body.input.some((item: { type?: string }) => item.type === "additional_tools"); - const rejection = { - type: "error", - error: { - code: "bad_request", - type: "websocket_error", - message: "additional_tools rejected", - }, - }; - const restricted = - (body.tool_choice as { type?: string } | undefined)?.type === "allowed_tools"; - let events: Array>; - if (restricted && input.includes("SDK_ALLOWLIST_REJECT_BEFORE")) { - events = [ - { - ...rejection, - error: { - ...rejection.error, - code: "invalid_request_body", - message: "allowed_tools rejected", - }, - }, - ]; - } else if (restricted && input.includes("SDK_ALLOWLIST_REJECT_AFTER")) { - events = [ - ...buildResponsesEvents("already streamed", id).slice(0, 4), - { - ...rejection, - error: { - ...rejection.error, - code: "invalid_request_body", - message: "allowed_tools rejected", - }, - }, - ]; - } else if (input.includes("SDK_REMOVAL_RESTORED")) { - events = buildResponsesEvents("SDK_REMOVAL_RESTORED_OK", id); - } else if (input.includes("SDK_ALLOWLIST_REJECT_BEFORE")) { - events = buildResponsesEvents("SDK_ALLOWLIST_RECOVERED", id); - } else if (positionedUpdate && input.includes("SDK_RESPONSES_REJECT_BEFORE")) { - events = [rejection]; - } else if (positionedUpdate && input.includes("SDK_RESPONSES_REJECT_AFTER")) { - events = [...buildResponsesEvents("already streamed", id).slice(0, 4), rejection]; - } else if (input.includes("SDK_RESPONSES_LEGACY_FOLLOW_UP")) { - events = buildResponsesEvents("SDK_RESPONSES_LEGACY_OK", id); - } else if (input.includes("SDK_RESPONSES_REJECT_BEFORE")) { - events = buildResponsesEvents("SDK_RESPONSES_RECOVERED", id); - } else if ( - input.includes("SDK_RESPONSES_AFTER") && - !input.includes("SDK_ADDED_TOOL_RESULT") - ) { - events = buildFunctionCallEvents(id); - } else { - events = buildResponsesEvents( - input.includes("SDK_ADDED_TOOL_RESULT") ? "SDK_RESPONSES_TOOL_OK" : WS_TEXT, - id - ); - } - for (const event of events) { + for (const event of toolUpdateEvents(body, id, WS_TEXT)) { socket.send(JSON.stringify(event)); } }); @@ -221,6 +241,7 @@ async function startFakeUpstream(): Promise<{ wsRequestCount: () => wsRequests, wsBodies: () => [...wsBodies], httpBodies: () => [...httpBodies], + chatBodies: () => [...chatBodies], async close() { wss.clients.forEach((c) => c.terminate()); await new Promise((resolve) => wss.close(() => resolve())); @@ -229,13 +250,107 @@ async function startFakeUpstream(): Promise<{ }; } -function buildFunctionCallEvents(id: string): Array> { +function toolUpdateEvents( + body: Record, + id: string, + defaultText: string +): Array> { + const input = JSON.stringify(body.input); + const positionedUpdate = + Array.isArray(body.input) && + body.input.some((item: { type?: string }) => item.type === "additional_tools"); + const rejection = { + type: "error", + error: { + code: "bad_request", + type: "websocket_error", + message: "additional_tools rejected", + }, + }; + const restricted = + (body.tool_choice as { type?: string } | undefined)?.type === "allowed_tools"; + if (positionedUpdate && input.includes("SDK_RESPONSES_REJECT_GENERIC")) { + return [{ type: "error", error: { message: "invalid request" } }]; + } + if (restricted && input.includes("SDK_ALLOWLIST_REJECT_BEFORE")) { + return [ + { + ...rejection, + error: { + ...rejection.error, + code: "invalid_request_body", + message: "allowed_tools rejected", + }, + }, + ]; + } + if (restricted && input.includes("SDK_ALLOWLIST_REJECT_AFTER")) { + return [ + ...buildResponsesEvents("already streamed", id).slice(0, 4), + { + ...rejection, + error: { + ...rejection.error, + code: "invalid_request_body", + message: "allowed_tools rejected", + }, + }, + ]; + } + if (input.includes("SDK_REMOVAL_RESTORED")) { + return buildResponsesEvents("SDK_REMOVAL_RESTORED_OK", id); + } + if (input.includes("SDK_REMOVAL_ALL")) { + return buildResponsesEvents(defaultText, id); + } + if (input.includes("SDK_ALLOWLIST_REJECT_BEFORE")) { + return buildResponsesEvents("SDK_ALLOWLIST_RECOVERED", id); + } + if (positionedUpdate && input.includes("SDK_RESPONSES_REJECT_BEFORE")) { + return [rejection]; + } + if (positionedUpdate && input.includes("SDK_RESPONSES_REJECT_AFTER")) { + return [...buildResponsesEvents("already streamed", id).slice(0, 4), rejection]; + } + if (input.includes("SDK_RESPONSES_LEGACY_FOLLOW_UP")) { + return buildResponsesEvents("SDK_RESPONSES_LEGACY_OK", id); + } + if (input.includes("SDK_RESPONSES_REJECT_BEFORE")) { + return buildResponsesEvents("SDK_RESPONSES_RECOVERED", id); + } + if (input.includes("MCP_RESPONSES_REMOVED_AGAIN")) { + return buildResponsesEvents("MCP_RESPONSES_REMOVED_OK", id); + } + if (input.includes("MCP_RESPONSES_AFTER") && !input.includes("MCP_ADDED_TOOL_RESULT")) { + return buildFunctionCallEvents( + id, + "a_server-get_env", + JSON.stringify({ name: "CACHE_TEST_RESULT" }) + ); + } + if (input.includes("MCP_ADDED_TOOL_RESULT")) { + return buildResponsesEvents("MCP_RESPONSES_TOOL_OK", id); + } + if (input.includes("SDK_RESPONSES_AFTER") && !input.includes("SDK_ADDED_TOOL_RESULT")) { + return buildFunctionCallEvents(id); + } + return buildResponsesEvents( + input.includes("SDK_ADDED_TOOL_RESULT") ? "SDK_RESPONSES_TOOL_OK" : defaultText, + id + ); +} + +function buildFunctionCallEvents( + id: string, + name = "sdk_epoch_added", + args = "{}" +): Array> { const call = { type: "function_call", id: "fc_sdk_added", call_id: "call_sdk_added", - name: "sdk_epoch_added", - arguments: "{}", + name, + arguments: args, status: "completed", }; return [ @@ -333,7 +448,8 @@ interface Counters { class TestHandler extends CopilotRequestHandler { constructor( private readonly upstreamUrl: string, - private readonly counters: Counters + private readonly counters: Counters, + private readonly failWebSocket = false ) { super(); } @@ -384,6 +500,9 @@ class TestHandler extends CopilotRequestHandler { protected override async openWebSocket( ctx: CopilotRequestContext ): Promise { + if (this.failWebSocket) { + throw new Error("Synthetic WebSocket handshake refused"); + } ctx.url = this.rewriteWsUrl(ctx.url); return new CountingSocketForwarder(ctx, this.counters); } @@ -417,7 +536,11 @@ describe("CopilotRequestHandler — single subclass handles HTTP + WebSocket", a wsResponseMessages: 0, }; - const { copilotClient: client, env } = await createSdkTestContext({ + const { + copilotClient: client, + env, + workDir, + } = await createSdkTestContext({ copilotClientOptions: { requestHandler: new TestHandler(upstream.url, counters), }, @@ -551,6 +674,470 @@ describe("CopilotRequestHandler — single subclass handles HTTP + WebSocket", a } }, 90_000); + const routes: Array<{ + name: string; + config: Partial; + bodies: () => Array>; + firstText: string; + }> = [ + { + name: "CAPI WebSocket", + config: { capi: { enableWebSocketResponses: true } }, + bodies: upstream.wsBodies, + firstText: WS_TEXT, + }, + { + name: "CAPI HTTP", + config: { capi: { enableWebSocketResponses: false } }, + bodies: upstream.httpBodies, + firstText: HTTP_TEXT, + }, + ...(["http", "websockets"] as const).map((transport) => ({ + name: `BYOK ${transport}`, + config: { + provider: { + type: "openai" as const, + wireApi: "responses" as const, + transport, + baseUrl: upstream.url, + apiKey: "synthetic-provider-key", + modelId: "gpt-6-sol", + wireModel: "gpt-6-sol", + }, + }, + bodies: transport === "http" ? upstream.httpBodies : upstream.wsBodies, + firstText: transport === "http" ? HTTP_TEXT : WS_TEXT, + })), + ]; + for (const route of routes) { + describe(route.name, async () => { + const routeClient = + route.config.capi?.enableWebSocketResponses === false + ? ( + await createSdkTestContext({ + copilotClientOptions: { + requestHandler: new TestHandler(upstream.url, counters), + // This runtime does not yet wire the session-level CAPI transport opt-out. + env: { COPILOT_CLI_DISABLE_WEBSOCKET_RESPONSES: "true" }, + }, + }) + ).copilotClient + : client; + it(`${route.config.provider ? "sends current tools" : "preserves the prefix"} on ${route.name} and executes a tool from a newly enabled MCP server`, async () => { + const config = mcpTestServerConfig("MCP_ADDED_TOOL_RESULT"); + await routeClient.start(); + const beforeRequests = route.bodies().length; + const session = await routeClient.createSession({ + model: "gpt-6-sol", + onPermissionRequest: approveAll, + ...route.config, + toolSearch: { enabled: false }, + mcpServers: { z_server: config, a_server: config }, + disabledMcpServers: ["a_server"], + }); + try { + await session.rpc.tools.initializeAndValidate(); + expect( + (await session.rpc.tools.getCurrentMetadata()).tools.map( + (tool) => tool.name + ) + ).toContain("z_server-get_env"); + expect( + (await session.rpc.tools.getCurrentMetadata()).tools.map( + (tool) => tool.name + ) + ).not.toContain("a_server-get_env"); + const first = await session.sendAndWait({ prompt: "MCP_RESPONSES_FIRST" }); + expect(first?.data.content).toContain(route.firstText); + + await session.rpc.mcp.setEnvValueMode({ mode: "direct" }); + await session.rpc.mcp.enable({ serverName: "a_server" }); + await session.rpc.tools.initializeAndValidate(); + expect( + (await session.rpc.tools.getCurrentMetadata()).tools.map( + (tool) => tool.name + ) + ).toContain("a_server-get_env"); + const second = await session.sendAndWait({ + prompt: "MCP_RESPONSES_AFTER: call a_server-get_env.", + }); + expect(second?.data.content).toContain("MCP_RESPONSES_TOOL_OK"); + + const requests = route.bodies().slice(beforeRequests); + expect(requests).toHaveLength(3); + const [before, update, afterTool] = requests; + expect(before.tools).toEqual( + expect.arrayContaining([ + expect.objectContaining({ name: "z_server-get_env" }), + ]) + ); + expect(JSON.stringify(before.tools)).not.toContain('"name":"a_server-get_env"'); + for (const request of [update, afterTool]) { + if (route.config.provider) { + expect(request.tools).toEqual( + expect.arrayContaining([ + expect.objectContaining({ name: "a_server-get_env" }), + expect.objectContaining({ name: "z_server-get_env" }), + ]) + ); + expect(request.tools).toEqual(update.tools); + } else { + expect(JSON.stringify(request.tools)).toBe( + JSON.stringify(before.tools) + ); + } + expect(request.instructions).toEqual(before.instructions); + } + expect(update.previous_response_id ?? null).toBeNull(); + const input = update.input as Array>; + expect(JSON.stringify(input[0])).toBe( + JSON.stringify((before.input as Array>)[0]) + ); + if (route.config.provider) { + expect(JSON.stringify(input)).not.toContain('"type":"additional_tools"'); + } else { + expect(input).toContainEqual( + expect.objectContaining({ + type: "additional_tools", + tools: expect.arrayContaining([ + expect.objectContaining({ name: "a_server-get_env" }), + ]), + }) + ); + const additionIndex = input.findIndex( + (item) => item.type === "additional_tools" + ); + expect(additionIndex).toBeGreaterThan(0); + expect(JSON.stringify(input.slice(0, additionIndex))).toContain( + "MCP_RESPONSES_AFTER" + ); + } + expect(JSON.stringify(afterTool.input)).toContain("MCP_ADDED_TOOL_RESULT"); + } finally { + await session.disconnect(); + } + }, 90_000); + + it(`removes MCP tools on ${route.name} and executes a restored tool`, async () => { + const toolName = "a_server-get_env"; + const survivor = "z_server-get_env"; + const config = mcpTestServerConfig("MCP_ADDED_TOOL_RESULT"); + await client.start(); + const beforeRequests = route.bodies().length; + const session = await routeClient.createSession({ + model: "gpt-6-sol", + onPermissionRequest: approveAll, + ...route.config, + toolSearch: { enabled: false }, + availableTools: [toolName, survivor], + mcpServers: { a_server: config, z_server: config }, + }); + try { + await session.rpc.mcp.setEnvValueMode({ mode: "direct" }); + await session.rpc.tools.initializeAndValidate(); + expect( + (await session.rpc.tools.getCurrentMetadata()).tools.map( + (tool) => tool.name + ) + ).toEqual(expect.arrayContaining([toolName, survivor])); + expect( + (await session.sendAndWait({ prompt: "MCP_RESPONSES_FIRST" }))?.data.content + ).toContain(route.firstText); + + await session.rpc.mcp.disable({ serverName: "a_server" }); + await session.rpc.tools.initializeAndValidate(); + // Tool metadata includes the previous request; the host reports live availability. + const disabled = await session.rpc.mcp.list(); + expect(disabled.host?.disabledServers).toContain("a_server"); + expect(disabled.host?.disabledServers).not.toContain("z_server"); + expect(disabled.servers).toContainEqual( + expect.objectContaining({ name: "a_server", status: "disabled" }) + ); + expect( + (await session.sendAndWait({ prompt: "MCP_RESPONSES_REMOVED" }))?.data + .content + ).toContain(route.firstText); + + await session.rpc.mcp.enable({ serverName: "a_server" }); + await session.rpc.tools.initializeAndValidate(); + expect( + (await session.rpc.tools.getCurrentMetadata()).tools.map( + (tool) => tool.name + ) + ).toContain(toolName); + expect( + ( + await session.sendAndWait({ + prompt: `MCP_RESPONSES_AFTER: call ${toolName}.`, + }) + )?.data.content + ).toContain("MCP_RESPONSES_TOOL_OK"); + + await session.rpc.mcp.disable({ serverName: "a_server" }); + await session.rpc.tools.initializeAndValidate(); + const disabledAgain = await session.rpc.mcp.list(); + expect(disabledAgain.host?.disabledServers).toContain("a_server"); + expect(disabledAgain.host?.disabledServers).not.toContain("z_server"); + expect(disabledAgain.servers).toContainEqual( + expect.objectContaining({ name: "a_server", status: "disabled" }) + ); + expect( + (await session.sendAndWait({ prompt: "MCP_RESPONSES_REMOVED_AGAIN" }))?.data + .content + ).toContain("MCP_RESPONSES_REMOVED_OK"); + + const requests = route.bodies().slice(beforeRequests); + expect(requests).toHaveLength(5); + const [before, removed, restored, afterTool, removedAgain] = requests; + expect(before.tools).toEqual( + expect.arrayContaining([ + expect.objectContaining({ name: toolName }), + expect.objectContaining({ name: survivor }), + ]) + ); + for (const request of [removed, restored, afterTool, removedAgain]) { + expect(request.instructions).toEqual(before.instructions); + expect(JSON.stringify(request.input)).not.toContain( + '"type":"additional_tools"' + ); + } + const choice = { + type: "allowed_tools", + mode: "auto", + tools: [{ type: "function", name: survivor }], + }; + for (const request of [removed, removedAgain]) { + if (route.config.provider) { + expect(request.tools).toEqual( + (before.tools as Array>).filter( + (tool) => tool.name !== toolName + ) + ); + expect(request.tool_choice).not.toEqual( + expect.objectContaining({ type: "allowed_tools" }) + ); + } else { + expect(JSON.stringify(request.tools)).toBe( + JSON.stringify(before.tools) + ); + expect(request.tool_choice).toEqual(choice); + } + expect(request.previous_response_id ?? null).toBeNull(); + } + for (const request of [restored, afterTool]) { + expect(JSON.stringify(request.tools)).toBe(JSON.stringify(before.tools)); + expect(request.tool_choice).not.toEqual( + expect.objectContaining({ type: "allowed_tools" }) + ); + } + expect(restored.previous_response_id ?? null).toBeNull(); + expect(JSON.stringify(afterTool.input)).toContain("MCP_ADDED_TOOL_RESULT"); + } finally { + await session.disconnect(); + } + }, 90_000); + }); + } + + for (const byok of [false, true]) { + it(`preserves ${byok ? "BYOK" : "CAPI"} Chat Completions tools through removal and restores real execution`, async () => { + const original = { + name: "sdk_chat_original", + description: "Original", + parameters: { type: "object", properties: {} }, + defer: "never", + } as const; + const removable = { ...original, name: "sdk_chat_removed", description: "Removable" }; + let calls = 0; + const start = upstream.chatBodies().length; + const session = await client.createSession({ + model: "gpt-5.5", + ...(byok + ? { + provider: { + type: "openai" as const, + wireApi: "completions" as const, + baseUrl: upstream.url, + apiKey: "synthetic-provider-key", + modelId: "gpt-5.5", + wireModel: "gpt-5.5", + }, + } + : {}), + toolSearch: { enabled: false }, + onPermissionRequest: approveAll, + availableTools: [original.name, removable.name], + tools: [ + defineTool(original.name, { + description: original.description, + handler: () => "SDK_CHAT_ORIGINAL_RESULT", + }), + defineTool(removable.name, { + description: removable.description, + handler: () => { + calls++; + return "SDK_CHAT_EXECUTED_RESULT"; + }, + }), + ], + }); + try { + await session.rpc.tools.initializeAndValidate(); + expect( + (await session.sendAndWait({ prompt: "SDK_CHAT_FIRST" }))?.data.content + ).toContain("SDK_CHAT_FIRST_OK"); + await session.rpc.tools.set({ tools: [original] }); + expect( + (await session.sendAndWait({ prompt: "SDK_CHAT_REMOVED" }))?.data.content + ).toContain("SDK_CHAT_REMOVED_OK"); + expect(calls).toBe(0); + const removedRequests = upstream.chatBodies().slice(start); + expect(removedRequests).toHaveLength(3); + const [before, removed, afterRejectedCall] = removedRequests; + expect(JSON.stringify(removed.tools)).toBe(JSON.stringify(before.tools)); + expect(removed.tool_choice).toEqual({ + type: "allowed_tools", + allowed_tools: { + mode: "auto", + tools: [{ type: "function", function: { name: original.name } }], + }, + }); + expect(JSON.stringify(afterRejectedCall.messages)).toContain("does not exist"); + await session.rpc.tools.set({ tools: [original, removable] }); + expect( + (await session.sendAndWait({ prompt: "SDK_CHAT_RESTORED" }))?.data.content + ).toContain("SDK_CHAT_RESTORED_OK"); + expect(calls).toBe(1); + const requests = upstream.chatBodies().slice(start); + expect(requests).toHaveLength(5); + for (const request of requests.slice(3)) { + expect(JSON.stringify(request.tools)).toBe(JSON.stringify(before.tools)); + expect(request.tool_choice).not.toEqual( + expect.objectContaining({ type: "allowed_tools" }) + ); + } + expect(JSON.stringify(requests[4].messages)).toContain("SDK_CHAT_EXECUTED_RESULT"); + } finally { + await session.disconnect(); + } + }); + } + + describe("WebSocket to HTTP tool-update fallback", async () => { + const { copilotClient: fallbackClient } = await createSdkTestContext({ + copilotClientOptions: { requestHandler: new TestHandler(upstream.url, counters, true) }, + }); + it("recovers an unrecognized HTTP 422 once and keeps later turns on current tools", async () => { + const original = { + name: "sdk_epoch_original", + description: "Original", + parameters: { type: "object", properties: {} }, + defer: "never", + } as const; + const added = { ...original, name: "sdk_epoch_added", description: "Added" }; + const start = upstream.httpBodies().length; + const session = await fallbackClient.createSession({ + model: "gpt-6-sol", + capi: { enableWebSocketResponses: true }, + toolSearch: { enabled: false }, + onPermissionRequest: approveAll, + tools: [ + defineTool(original.name, { handler: () => "original" }), + defineTool(added.name, { handler: () => "added" }), + ], + }); + try { + await session.rpc.tools.initializeAndValidate(); + await session.rpc.tools.set({ tools: [original] }); + expect( + (await session.sendAndWait({ prompt: "SDK_RESPONSES_FIRST" }))?.data.content + ).toContain(HTTP_TEXT); + await session.rpc.tools.set({ tools: [original, added] }); + expect( + (await session.sendAndWait({ prompt: "SDK_RESPONSES_REJECT_GENERIC" }))?.data + .content + ).toContain(HTTP_TEXT); + expect( + (await session.sendAndWait({ prompt: "SDK_RESPONSES_LEGACY_FOLLOW_UP" }))?.data + .content + ).toContain("SDK_RESPONSES_LEGACY_OK"); + const requests = upstream.httpBodies().slice(start); + expect(requests).toHaveLength(4); + const [before, rejected, recovered, later] = requests; + expect(JSON.stringify(rejected.tools)).toBe(JSON.stringify(before.tools)); + expect(JSON.stringify(rejected.input)).toContain('"type":"additional_tools"'); + for (const request of [recovered, later]) { + expect(JSON.stringify(request.input)).not.toContain( + '"type":"additional_tools"' + ); + expect(request.tools).toEqual( + expect.arrayContaining([expect.objectContaining({ name: added.name })]) + ); + } + } finally { + await session.disconnect(); + } + }); + }); + + it("rebases a refreshed MCP definition before executing the restarted server", async () => { + const toolName = "a_server-get_env"; + const descriptionFile = join(workDir, "tool-description.txt"); + await writeFile(descriptionFile, "MCP_ORIGINAL_DESCRIPTION"); + await client.start(); + const beforeRequests = upstream.wsBodies().length; + const session = await client.createSession({ + model: "gpt-6-sol", + onPermissionRequest: approveAll, + capi: { enableWebSocketResponses: true }, + toolSearch: { enabled: false }, + mcpServers: { + a_server: mcpTestServerConfig("MCP_ADDED_TOOL_RESULT", descriptionFile), + }, + }); + try { + await session.rpc.mcp.setEnvValueMode({ mode: "direct" }); + await session.rpc.tools.initializeAndValidate(); + expect( + (await session.rpc.tools.getCurrentMetadata()).tools.find( + (tool) => tool.name === toolName + )?.description + ).toContain("MCP_ORIGINAL_DESCRIPTION"); + expect( + (await session.sendAndWait({ prompt: "MCP_RESPONSES_FIRST" }))?.data.content + ).toContain(WS_TEXT); + + await writeFile(descriptionFile, "MCP_REPLACEMENT_DESCRIPTION"); + await session.rpc.mcp.restartServer({ serverName: "a_server" }); + await session.rpc.tools.initializeAndValidate(); + expect( + (await session.rpc.tools.getCurrentMetadata()).tools.find( + (tool) => tool.name === toolName + )?.description + ).toContain("MCP_REPLACEMENT_DESCRIPTION"); + expect( + (await session.sendAndWait({ prompt: `MCP_RESPONSES_AFTER: call ${toolName}.` })) + ?.data.content + ).toContain("MCP_RESPONSES_TOOL_OK"); + + const requests = upstream.wsBodies().slice(beforeRequests); + expect(requests).toHaveLength(3); + const [before, update, afterTool] = requests; + expect(JSON.stringify(before.tools)).toContain("MCP_ORIGINAL_DESCRIPTION"); + expect(JSON.stringify(update.tools)).toContain("MCP_REPLACEMENT_DESCRIPTION"); + expect(JSON.stringify(update.tools)).not.toContain("MCP_ORIGINAL_DESCRIPTION"); + expect(update.instructions).toEqual(before.instructions); + expect(update.previous_response_id ?? null).toBeNull(); + for (const request of [update, afterTool]) { + expect(JSON.stringify(request.input)).not.toContain('"type":"additional_tools"'); + } + expect(JSON.stringify(afterTool.tools)).toBe(JSON.stringify(update.tools)); + expect(JSON.stringify(afterTool.input)).toContain("MCP_ADDED_TOOL_RESULT"); + } finally { + await session.disconnect(); + } + }, 90_000); + it("keeps the SDK removal prefix while restricting tools across continuations and restoration", async () => { const original = { name: "sdk_epoch_original", @@ -619,12 +1206,12 @@ describe("CopilotRequestHandler — single subclass handles HTTP + WebSocket", a expect( (await session.sendAndWait({ prompt: "SDK_REMOVAL_ALL" }))?.data.content ).toContain(HTTP_TEXT); - // No remaining tools selects the runtime's single-shot HTTP route. + // No remaining tools selects HTTP, which keeps the prefix and forbids all calls. const allRemoved = upstream .httpBodies() .findLast((body) => JSON.stringify(body.input).includes("SDK_REMOVAL_ALL"))!; - expect(allRemoved.tools).toEqual([]); - expect(allRemoved.tool_choice).toBeUndefined(); + expect(JSON.stringify(allRemoved.tools)).toBe(JSON.stringify(before.tools)); + expect(allRemoved.tool_choice).toBe("none"); expect(allRemoved.previous_response_id ?? null).toBeNull(); await session.rpc.tools.set({ tools: [original, added] }); diff --git a/nodejs/test/e2e/customization_directories.e2e.test.ts b/nodejs/test/e2e/customization_directories.e2e.test.ts new file mode 100644 index 0000000000..2a3adefa51 --- /dev/null +++ b/nodejs/test/e2e/customization_directories.e2e.test.ts @@ -0,0 +1,387 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { execFileSync } from "node:child_process"; +import fs from "node:fs"; +import path from "node:path"; +import { beforeEach, describe, expect, it } from "vitest"; +import { approveAll, RuntimeConnection } from "../../src/index.js"; +import { createSdkTestContext } from "./harness/sdkTestContext.js"; + +function writeFile(root: string, relative: string[], content: string): string { + const file = path.join(root, ...relative); + fs.mkdirSync(path.dirname(file), { recursive: true }); + fs.writeFileSync(file, content); + return file; +} + +function writeSkill(root: string, directory: string, name: string): void { + writeFile( + root, + [directory, name, "SKILL.md"], + `---\nname: ${name}\ndescription: Discovery directory regression fixture.\n---\n\nUse this skill.\n` + ); +} + +describe("Customization directory discovery", async () => { + const { + copilotClient: client, + env, + workDir: testRoot, + createClient, + } = await createSdkTestContext({ + copilotClientOptions: { env: { COPILOT_ALLOW_ALL: "1" } }, + }); + let workDir: string; + + beforeEach(async () => { + workDir = fs.realpathSync.native(fs.mkdtempSync(path.join(testRoot, "customizations-"))); + execFileSync("git", ["init", "--quiet"], { cwd: workDir, windowsHide: true }); + writeFile( + env.COPILOT_HOME, + ["config.json"], + JSON.stringify({ trustedFolders: [testRoot] }) + ); + await client.start(); + }); + + it("discovers all four repository lists and applies independent local replacements", async () => { + writeSkill(workDir, "vendor-skills", "repository-directory-skill"); + writeFile( + workDir, + ["vendor-agents", "directory.agent.md"], + "---\nname: repository-directory-agent\ndescription: Directory fixture.\n---\n\nRead the repository.\n" + ); + const instruction = writeFile( + workDir, + ["vendor-instructions", "guard.instructions.md"], + "---\napplyTo: '**/*.ts'\n---\n\nRepository directory instruction sentinel.\n" + ); + writeFile( + workDir, + ["vendor-instructions", "AGENTS.md"], + "Do not inject this package hierarchy." + ); + writeFile( + workDir, + ["vendor-hooks", "hooks.json"], + JSON.stringify({ hooks: { sessionStart: [{ command: "echo directory-hook" }] } }) + ); + writeFile( + workDir, + [".github", "copilot", "settings.json"], + JSON.stringify({ + skillDirectories: ["vendor-skills"], + agentDirectories: ["vendor-agents"], + instructionDirectories: ["vendor-instructions"], + hookDirectories: ["vendor-hooks"], + }) + ); + const projectPaths = [workDir]; + const skills = await client.rpc.skills.discover({ projectPaths, excludeHostSkills: true }); + expect( + skills.skills.filter((skill) => skill.name === "repository-directory-skill") + ).toHaveLength(1); + const agents = await client.rpc.agents.discover({ projectPaths, excludeHostAgents: true }); + expect( + agents.agents.filter((agent) => agent.name === "repository-directory-agent") + ).toHaveLength(1); + const instructions = await client.rpc.instructions.discover({ + projectPaths, + excludeHostInstructions: true, + }); + expect( + instructions.sources.filter((source) => source.sourcePath === instruction) + ).toHaveLength(1); + expect( + instructions.sources.some((source) => + source.content.includes("Do not inject this package hierarchy.") + ) + ).toBe(false); + const hooks = await client.rpc.hooks.discover({ projectPaths, excludeHostHooks: true }); + expect(hooks.hooks.some((hook) => hook.source.includes("vendor-hooks"))).toBe(true); + + writeFile( + workDir, + [".github", "copilot", "settings.local.json"], + JSON.stringify({ skillDirectories: [], hookDirectories: [] }) + ); + const clearedSkills = await client.rpc.skills.discover({ + projectPaths, + excludeHostSkills: true, + }); + expect( + clearedSkills.skills.some((skill) => skill.name === "repository-directory-skill") + ).toBe(false); + const retainedAgents = await client.rpc.agents.discover({ + projectPaths, + excludeHostAgents: true, + }); + expect( + retainedAgents.agents.some((agent) => agent.name === "repository-directory-agent") + ).toBe(true); + const clearedHooks = await client.rpc.hooks.discover({ + projectPaths, + excludeHostHooks: true, + }); + expect(clearedHooks.hooks.some((hook) => hook.source.includes("vendor-hooks"))).toBe(false); + }); + + it("anchors SDK relative roots once and reanchors only newly supplied lists", async () => { + const original = path.join(workDir, "original"); + const next = path.join(workDir, "next"); + writeSkill(original, "skills", "original-directory-skill"); + writeSkill(next, "skills", "next-directory-skill"); + const session = await client.createSession({ + onPermissionRequest: approveAll, + workingDirectory: original, + enableConfigDiscovery: false, + skillDirectories: ["skills"], + }); + try { + await session.rpc.skills.ensureLoaded(); + expect( + (await session.rpc.skills.list()).skills.some( + (skill) => skill.name === "original-directory-skill" + ) + ).toBe(true); + expect((await session.rpc.options.update({ workingDirectory: next })).success).toBe( + true + ); + await session.rpc.skills.ensureLoaded(); + const retained = await session.rpc.skills.list(); + expect(retained.skills.some((skill) => skill.name === "original-directory-skill")).toBe( + true + ); + expect(retained.skills.some((skill) => skill.name === "next-directory-skill")).toBe( + false + ); + expect( + (await session.rpc.options.update({ skillDirectories: ["skills"] })).success + ).toBe(true); + await session.rpc.skills.ensureLoaded(); + const changed = await session.rpc.skills.list(); + expect(changed.skills.some((skill) => skill.name === "original-directory-skill")).toBe( + false + ); + expect(changed.skills.some((skill) => skill.name === "next-directory-skill")).toBe( + true + ); + expect((await session.rpc.options.update({ skillDirectories: [] })).success).toBe(true); + await session.rpc.skills.ensureLoaded(); + expect( + (await session.rpc.skills.list()).skills.some( + (skill) => skill.name === "next-directory-skill" + ) + ).toBe(false); + } finally { + await session.disconnect(); + } + }); + + it("anchors newly supplied roots to the persisted workspace on cold resume", async () => { + const original = path.join(workDir, "nested"); + fs.mkdirSync(original); + writeSkill(workDir, path.join("vendor", "alternate-skills"), "cold-resume-directory-skill"); + const first = createClient({ connection: RuntimeConnection.forStdio() }); + const next = createClient({ connection: RuntimeConnection.forStdio() }); + try { + await first.start(); + const session = await first.createSession({ + onPermissionRequest: approveAll, + workingDirectory: original, + enableConfigDiscovery: false, + requestExtensions: false, + }); + await session.rpc.workspaces.ensure({}); + const { sessionId } = await first.rpc.sessions.fork({ sessionId: session.sessionId }); + await session.disconnect(); + await first.stop(); + await next.start(); + const resumed = await next.resumeSession(sessionId, { + onPermissionRequest: approveAll, + enableConfigDiscovery: false, + requestExtensions: false, + skillDirectories: [path.join("..", "vendor", "alternate-skills")], + }); + try { + expect((await resumed.rpc.metadata.snapshot()).workingDirectory).toBe(original); + await resumed.rpc.skills.ensureLoaded(); + const skills = await resumed.rpc.skills.list(); + expect( + skills.skills.some((skill) => skill.name === "cold-resume-directory-skill") + ).toBe(true); + } finally { + await resumed.disconnect(); + } + } finally { + await next.stop(); + await first.stop(); + } + }); + + it("retains explicit file hooks when repository hooks and ambient discovery are disabled", async () => { + const hooks = path.join(workDir, "explicit-hooks"); + writeFile(hooks, ["resource.txt"], "resource-sentinel"); + writeFile( + hooks, + ["probe.cjs"], + "const fs = require('node:fs'); const path = require('node:path');\n" + + "fs.appendFileSync(path.join(process.cwd(), 'hook-observations.jsonl'), JSON.stringify({" + + "directory: process.env.COPILOT_HOOK_DIR, cwd: process.cwd(), " + + "resource: fs.readFileSync(path.join(process.env.COPILOT_HOOK_DIR, 'resource.txt'), 'utf8')" + + "}) + '\\n');\n" + ); + writeFile( + hooks, + ["hooks.json"], + JSON.stringify({ + hooks: { + preToolUse: [ + { + type: "command", + bash: `"${process.execPath}" "$COPILOT_HOOK_DIR/probe.cjs"`, + powershell: `& "${process.execPath}" (Join-Path $env:COPILOT_HOOK_DIR 'probe.cjs')`, + }, + ], + }, + }) + ); + writeFile( + workDir, + [".github", "hooks", "ambient.json"], + JSON.stringify({ + hooks: { preToolUse: [{ command: "echo ambient-hook" }] }, + }) + ); + let callbackInvocations = 0; + const session = await client.createSession({ + onPermissionRequest: approveAll, + workingDirectory: workDir, + enableConfigDiscovery: false, + enableFileHooks: false, + hookDirectories: [hooks], + hooks: { + onPreToolUse: async () => { + callbackInvocations++; + return { permissionDecision: "allow" }; + }, + }, + }); + try { + await client.rpc.sessions.reloadPluginHooks({ + sessionId: session.sessionId, + deferRepoHooks: true, + }); + await session.rpc.tools.initializeAndValidate(); + const result = await session.rpc.tools.execute({ + name: "view", + arguments: { path: path.join(hooks, "resource.txt") }, + toolCallId: "explicit-hook-directory-probe", + }); + expect(JSON.stringify(result)).toContain("resource-sentinel"); + expect(callbackInvocations).toBe(1); + const observations = path.join(workDir, "hook-observations.jsonl"); + const first = fs.readFileSync(observations, "utf8").trim().split("\n"); + expect(first).toHaveLength(1); + expect(JSON.parse(first[0])).toEqual({ + directory: hooks, + cwd: workDir, + resource: "resource-sentinel", + }); + expect((await session.rpc.options.update({ hookDirectories: [] })).success).toBe(true); + await client.rpc.sessions.reloadPluginHooks({ + sessionId: session.sessionId, + deferRepoHooks: true, + }); + await session.rpc.tools.execute({ + name: "view", + arguments: { path: path.join(hooks, "resource.txt") }, + toolCallId: "cleared-hook-directory-probe", + }); + expect(fs.readFileSync(observations, "utf8").trim().split("\n")).toHaveLength(1); + expect(callbackInvocations).toBe(2); + } finally { + await session.disconnect(); + } + }); + + it("retains conventional and configured guard hooks after changing to a repository subdirectory", async () => { + const observations = path.join(workDir, "cwd-hooks.jsonl"); + const subdir = path.join(workDir, "subdir"); + fs.mkdirSync(subdir); + for (const parts of [[".github", "hooks"], ["vendor-hooks"]]) { + const hooks = path.join(workDir, ...parts); + writeFile( + hooks, + ["probe.cjs"], + "require('node:fs').appendFileSync(" + + JSON.stringify(observations) + + ", JSON.stringify({directory: process.env.COPILOT_HOOK_DIR, cwd: process.cwd()}) + '\\n');\n" + ); + writeFile( + hooks, + ["hooks.json"], + JSON.stringify({ + hooks: { + preToolUse: [ + { + type: "command", + bash: `"${process.execPath}" "$COPILOT_HOOK_DIR/probe.cjs"`, + powershell: `& "${process.execPath}" (Join-Path $env:COPILOT_HOOK_DIR 'probe.cjs')`, + }, + ], + }, + }) + ); + } + writeFile( + workDir, + [".github", "copilot", "settings.json"], + JSON.stringify({ hookDirectories: ["vendor-hooks"] }) + ); + const resource = writeFile(workDir, ["resource.txt"], "cwd-hook-resource"); + const session = await client.createSession({ + onPermissionRequest: approveAll, + workingDirectory: workDir, + enableConfigDiscovery: true, + requestExtensions: false, + }); + try { + await session.rpc.tools.initializeAndValidate(); + await session.rpc.tools.execute({ + name: "view", + arguments: { path: resource }, + toolCallId: "guard-before-cwd-change", + }); + expect(fs.readFileSync(observations, "utf8").trim().split("\n")).toHaveLength(2); + expect( + await session.rpc.metadata.setWorkingDirectory({ workingDirectory: subdir }) + ).toMatchObject({ workingDirectory: subdir }); + await session.rpc.tools.execute({ + name: "view", + arguments: { path: resource }, + toolCallId: "guard-after-cwd-change", + }); + const rows = fs + .readFileSync(observations, "utf8") + .trim() + .split("\n") + .map((row) => JSON.parse(row)); + expect(rows).toHaveLength(4); + expect( + rows + .slice(2) + .map((row) => row.directory) + .sort() + ).toEqual( + [path.join(workDir, ".github", "hooks"), path.join(workDir, "vendor-hooks")].sort() + ); + // Hooks without an explicit cwd remain anchored to the consuming project root. + expect(rows.slice(2).map((row) => row.cwd)).toEqual([workDir, workDir]); + } finally { + await session.disconnect(); + } + }); +}); diff --git a/nodejs/test/e2e/disabled_mcp_servers.e2e.test.ts b/nodejs/test/e2e/disabled_mcp_servers.e2e.test.ts index 5a240bbd76..e62de0e5d9 100644 --- a/nodejs/test/e2e/disabled_mcp_servers.e2e.test.ts +++ b/nodejs/test/e2e/disabled_mcp_servers.e2e.test.ts @@ -721,11 +721,26 @@ describe("disabled MCP servers", async () => { onPermissionRequest: approveAll, enableSessionStore: true, pluginDirectories: [pluginDirectory], - disabledMcpServers: ["disabled"], + disabledMcpServers: ["disabled", "github-mcp-server", "computer-use"], }); await waitForMcpStatus(resumedSession, "control", "connected"); await waitForMcpStatus(resumedSession, "disabled", "disabled"); await waitForMarkerCount(controlMarker, 1); + const inventory = await resumedSession.rpc.mcp.listConfigured(); + expect( + inventory.servers + .filter((server) => ["control", "disabled"].includes(server.name)) + .map(({ name, enabled }) => ({ name, enabled })) + .sort((a, b) => a.name.localeCompare(b.name)) + ).toEqual([ + { name: "control", enabled: true }, + { name: "disabled", enabled: false }, + ]); + expect( + inventory.servers.filter( + (server) => server.source === "builtin" && server.enabled + ) + ).toEqual([]); expect(existsSync(disabledMarker)).toBe(false); } finally { await initialClient.stop().catch(() => {}); diff --git a/nodejs/test/e2e/entra_identity_first.e2e.test.ts b/nodejs/test/e2e/entra_identity_first.e2e.test.ts index 2a6e49fff2..7df4d67c50 100644 --- a/nodejs/test/e2e/entra_identity_first.e2e.test.ts +++ b/nodejs/test/e2e/entra_identity_first.e2e.test.ts @@ -5,11 +5,12 @@ import { randomUUID } from "node:crypto"; import { mkdir, readFile, writeFile } from "node:fs/promises"; import { join } from "node:path"; -import { describe, expect, it, onTestFinished } from "vitest"; +import { describe, expect, it, onTestFailed, onTestFinished } from "vitest"; import { approveAll, CopilotClient, RuntimeConnection } from "../../src/index.js"; import { createSdkTestContext } from "./harness/sdkTestContext.js"; import { ConnectProxy } from "../../../test/harness/connectProxy.js"; import { createE2eRequestHandler } from "../../../test/harness/mockHandlers.js"; +import { readDiagnosticTail } from "../../../test/harness/sdk-diagnostics.mjs"; const { env, workDir, openAiEndpoint } = await createSdkTestContext({ useStdio: true, @@ -20,6 +21,19 @@ const M365_SCOPE = "42c8f7bd-c7a8-4ba9-bc1c-91edfb716247/.default"; const LOKI_HOST = "loki.identity-first.example"; const VERIFICATION_UNAVAILABLE = "Microsoft sign-in succeeded, but GitHub account verification is temporarily unavailable. Please try again later. Your active account has not changed."; +type CatalogReceipt = { + resource: "capi_models" | "loki_models" | "loki_quota"; + authenticated: boolean; + receivedAtMs: number; + status?: number; + /** Fixture-selected class, not proof that the runtime decoded the response. */ + responseClass?: "catalog" | "quota" | "mos_disabled" | "access_denied"; + replyAtMs?: number; + finishedAtMs?: number; + closedAtMs?: number; + closedBeforeFinish?: boolean; + requestAborted?: boolean; +}; const scenarios = [ { name: "both", emuGrant: true, staff: true, capi: 200, loki: 200, expected: "choices" }, { name: "emu only", emuGrant: true, staff: true, capi: 200, loki: 403, expected: "entraEmu" }, @@ -198,12 +212,70 @@ describe("Entra identity-first resource discovery", () => { } const grants: URLSearchParams[] = []; const catalogs: string[] = []; + const receipts: CatalogReceipt[] = []; + let omittedReceipts = 0; + let loginOutcome: + | "pending" + | "fulfilled" + | "rejected_no_backend" + | "rejected_verification" + | "rejected_rpc_timeout" + | "rejected_other" = "pending"; + const startedAt = performance.now(); + const elapsedMs = () => Math.round(performance.now() - startedAt); + onTestFailed(async () => { + console.error( + `[Entra discovery fixture] ${JSON.stringify({ loginOutcome, receipts, omittedReceipts })}` + ); + try { + const diagnostics = await readDiagnosticTail(profile); + console.error( + `[SDK diagnostics: last ${diagnostics.length} audited records]\n${diagnostics.join("\n")}` + ); + } catch { + console.error("SDK diagnostics unavailable"); + } + }); let latestRefresh = "identity-refresh"; const fallback = createE2eRequestHandler({ capiProxyUrl: openAiEndpoint.url }); const proxy = new ConnectProxy( async (request, response, host) => { const path = request.url ?? "/"; - const reply = (status: number, body: unknown) => { + const resource = + host === "api.githubcopilot.com" && path === "/models" + ? "capi_models" + : host === LOKI_HOST && path === "/api/models" + ? "loki_models" + : host === LOKI_HOST && path === "/api/quota" + ? "loki_quota" + : undefined; + const receipt: CatalogReceipt | undefined = resource + ? { resource, authenticated: false, receivedAtMs: elapsedMs() } + : undefined; + if (receipt) { + if (receipts.length < 32) receipts.push(receipt); + else omittedReceipts++; + request.once("aborted", () => { + receipt.requestAborted = true; + }); + response.once("finish", () => { + receipt.finishedAtMs = elapsedMs(); + }); + response.once("close", () => { + receipt.closedAtMs = elapsedMs(); + receipt.closedBeforeFinish = !response.writableFinished; + }); + } + const reply = ( + status: number, + body: unknown, + responseClass?: CatalogReceipt["responseClass"] + ) => { + if (receipt) { + receipt.status = status; + receipt.responseClass = responseClass; + receipt.replyAtMs = elapsedMs(); + } response.writeHead(status, { "content-type": "application/json" }); response.end(JSON.stringify(body)); return true; @@ -291,50 +363,69 @@ describe("Entra identity-first resource discovery", () => { if (host === "api.githubcopilot.com" && path === "/models") { expect(request.headers.authorization).toBe(`Bearer ${emuToken}`); catalogs.push("capi"); - return reply(scenario.capi, { - data: - "emptyCapi" in scenario && scenario.emptyCapi - ? [] - : [ - { - id: "gpt-5.2", - name: "Candidate CAPI model", - capabilities: { supports: { tool_calls: true } }, - model_picker_enabled: !( - "hiddenCapi" in scenario && scenario.hiddenCapi - ), - ...("restrictedCapi" in scenario - ? { billing: { restricted_to: ["enterprise"] } } - : {}), - }, - ], - }); + if (receipt) receipt.authenticated = true; + return reply( + scenario.capi, + { + data: + "emptyCapi" in scenario && scenario.emptyCapi + ? [] + : [ + { + id: "gpt-5.2", + name: "Candidate CAPI model", + capabilities: { supports: { tool_calls: true } }, + model_picker_enabled: !( + "hiddenCapi" in scenario && + scenario.hiddenCapi + ), + ...("restrictedCapi" in scenario + ? { + billing: { + restricted_to: ["enterprise"], + }, + } + : {}), + }, + ], + }, + scenario.capi === 200 ? "catalog" : "access_denied" + ); } if (host === LOKI_HOST && (path === "/api/models" || path === "/api/quota")) { expect(request.headers.authorization).toBe("Bearer m365-resource-token"); + if (receipt) receipt.authenticated = true; if ("mosDenied" in scenario && path === `/api/${scenario.mosDenied}`) { if (scenario.mosDenied === "models") catalogs.push("loki"); - return reply(403, { - error: { - message: - "Copilot CLI Entra access is not enabled for this user.", - type: "invalid_request_error", - code: "copilot_cli_entra_access_disabled", + return reply( + 403, + { + error: { + message: + "Copilot CLI Entra access is not enabled for this user.", + type: "invalid_request_error", + code: "copilot_cli_entra_access_disabled", + }, }, - }); + "mos_disabled" + ); } - if (path === "/api/quota") return reply(200, {}); + if (path === "/api/quota") return reply(200, {}, "quota"); catalogs.push("loki"); - return reply(scenario.loki, { - data: [ - { - id: "loki-test", - name: "Candidate M365 model", - capabilities: { supports: { tool_calls: true } }, - model_picker_enabled: true, - }, - ], - }); + return reply( + scenario.loki, + { + data: [ + { + id: "loki-test", + name: "Candidate M365 model", + capabilities: { supports: { tool_calls: true } }, + model_picker_enabled: true, + }, + ], + }, + scenario.loki === 200 ? "catalog" : "access_denied" + ); } return fallback(request, response, host); }, @@ -364,9 +455,11 @@ describe("Entra identity-first resource discovery", () => { connection: RuntimeConnection.forStdio({ path: process.env.COPILOT_CLI_PATH }), workingDirectory: workDir, useLoggedInUser: false, + logLevel: "debug" as const, env: { ...env, COPILOT_HOME: profile, + RUST_LOG: "error,sdk_diagnostics=debug", COPILOT_DISABLE_KEYTAR: "1", GH_TOKEN: "", GITHUB_TOKEN: "", @@ -393,6 +486,20 @@ describe("Entra identity-first resource discovery", () => { configDirectory: profile, onPermissionRequest: approveAll, }); + if (process.platform === "linux") { + await expect(session.rpc.accounts.login.begin({ kind: "entra" })).rejects.toThrow( + "only on Windows and macOS" + ); + expect(grants).toEqual([]); + expect(catalogs).toEqual([]); + expect( + await session.rpc.accounts.get({ query: { kind: "activeAccount" } }) + ).toEqual({ + kind: "activeAccount", + account: null, + }); + return; + } const { flowId } = await session.rpc.accounts.login.begin({ kind: "entra" }); const step = await session.rpc.accounts.login.advance({ flowId }); expect(step.kind).toBe("open-url"); @@ -414,6 +521,21 @@ describe("Entra identity-first resource discovery", () => { } return step; }); + void completing.then( + () => { + loginOutcome = "fulfilled"; + }, + (error: unknown) => { + const message = error instanceof Error ? error.message : ""; + loginOutcome = message.includes("No usable inference backend") + ? "rejected_no_backend" + : message.includes(VERIFICATION_UNAVAILABLE) + ? "rejected_verification" + : /timed out|timeout/i.test(message) + ? "rejected_rpc_timeout" + : "rejected_other"; + } + ); // Attach the failure assertion before the callback can complete the request. const completed = scenario.expected === "failure" diff --git a/nodejs/test/e2e/entra_renewal.e2e.test.ts b/nodejs/test/e2e/entra_renewal.e2e.test.ts index e040d8a905..82f20c6a27 100644 --- a/nodejs/test/e2e/entra_renewal.e2e.test.ts +++ b/nodejs/test/e2e/entra_renewal.e2e.test.ts @@ -197,6 +197,16 @@ it("requires linked sign-in, adopts same-session re-login, and persists silent r ); expect(modelRequests).toEqual([]); expect(grants).toBe(0); + if (process.platform === "linux") { + await expect(session.rpc.accounts.login.begin({ kind: "entra" })).rejects.toThrow( + "only on Windows and macOS" + ); + expect(signIns).toBe(0); + expect(exchanges).toBe(0); + expect(grants).toBe(0); + expect(modelRequests).toEqual([]); + return; + } const signIn = async () => { const { flowId } = await session.rpc.accounts.login.begin({ kind: "entra" }); diff --git a/nodejs/test/e2e/harness/mcpTestServer.ts b/nodejs/test/e2e/harness/mcpTestServer.ts new file mode 100644 index 0000000000..76e50a5837 --- /dev/null +++ b/nodejs/test/e2e/harness/mcpTestServer.ts @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +export function mcpTestServerConfig(result: string, descriptionFile?: string) { + const serverPath = resolve( + dirname(fileURLToPath(import.meta.url)), + "../../../../test/harness/test-mcp-server.mjs" + ); + return { + type: "stdio" as const, + command: process.execPath, + args: [ + serverPath, + ...(descriptionFile ? ["--tool-description-file", descriptionFile] : []), + ], + workingDirectory: dirname(serverPath), + env: { CACHE_TEST_RESULT: result }, + tools: ["get_env"], + }; +} diff --git a/nodejs/test/e2e/harness/sdkTestContext.ts b/nodejs/test/e2e/harness/sdkTestContext.ts index 1bbc4ab19e..515177ee44 100644 --- a/nodejs/test/e2e/harness/sdkTestContext.ts +++ b/nodejs/test/e2e/harness/sdkTestContext.ts @@ -3,10 +3,11 @@ *--------------------------------------------------------------------------------------------*/ import fs, { realpathSync } from "fs"; -import { rm } from "fs/promises"; +import { readdir, rm } from "fs/promises"; import os from "os"; import { basename, dirname, join, resolve } from "path"; import { rimraf } from "rimraf"; +import { createInterface } from "readline"; import { fileURLToPath } from "url"; import { afterAll, afterEach, beforeEach, onTestFailed, onTestFinished, TestContext } from "vitest"; import { CopilotClient, CopilotClientOptions, RuntimeConnection } from "../../../src"; @@ -16,6 +17,7 @@ import { formatError, retry } from "./sdkTestHelper"; import { resolvePreparedRuntimePath } from "../../../scripts/prepare-runtime.js"; import { testBackend, withTestBackend } from "./testBackend"; import { readDiagnosticTail } from "../../../../test/harness/sdk-diagnostics.mjs"; +import type { ReplayBackend } from "../../../../test/harness/replayingCapiProxy"; export const isCI = process.env.GITHUB_ACTIONS === "true"; export const DEFAULT_GITHUB_TOKEN = "fake-token-for-e2e-tests"; @@ -65,6 +67,7 @@ export async function createSdkTestContext({ copilotClientOptions, replayOnly = process.env.COPILOT_SDK_E2E_REPLAY_ONLY === "true", modelNames, + backend = testBackend, }: { logLevel?: "error" | "none" | "warning" | "info" | "debug" | "all"; cliPath?: string; @@ -72,6 +75,8 @@ export async function createSdkTestContext({ copilotClientOptions?: CopilotClientOptions; replayOnly?: boolean; modelNames?: Record; + /** Pin a provider-specific contract instead of inheriting the CI backend matrix. */ + backend?: ReplayBackend; } = {}) { const homeDir = realpathSync(fs.mkdtempSync(join(os.tmpdir(), "copilot-test-config-"))); const copilotHomeDir = realpathSync(fs.mkdtempSync(join(os.tmpdir(), "copilot-test-home-"))); @@ -170,11 +175,11 @@ export async function createSdkTestContext({ class TestClient extends CopilotClient { override createSession(config: SessionConfig) { - return super.createSession(withTestBackend(config, testBackend, proxyUrl)); + return super.createSession(withTestBackend(config, backend, proxyUrl)); } override resumeSession(sessionId: string, config: ResumeSessionConfig) { - return super.resumeSession(sessionId, withTestBackend(config, testBackend, proxyUrl)); + return super.resumeSession(sessionId, withTestBackend(config, backend, proxyUrl)); } } @@ -273,6 +278,30 @@ export async function createSdkTestContext({ console.error( `[SDK diagnostics: last ${diagnostics.length} audited records]\n${diagnostics.join("\n")}` ); + const logDir = join(copilotHomeDir, "logs"); + for (const file of await readdir(logDir)) { + if (!file.endsWith(".log")) continue; + const lines = createInterface({ + input: fs.createReadStream(join(logDir, file)), + }); + const mcpClosureDiagnostics: string[] = []; + for await (const line of lines) { + const mcpClosure = line.match( + /^\S+ \[(?:DEBUG|WARN)\] \[rust:mcp::client\] MCP (?:liveness probe (?:found the (?:session expired|transport (?:closed|channel closed))|failed; awaiting corroboration|went unanswered; inconclusive|could not be sent in time; inconclusive|failed inconclusively|failed with an unrecognised transport error)|liveness probes are neither proving life nor consistently failing|session reported closed)/ + ); + if (mcpClosure) { + // Keep only static messages; transport error fields can contain user data. + mcpClosureDiagnostics.push(mcpClosure[0]); + if (mcpClosureDiagnostics.length > 32) + mcpClosureDiagnostics.shift(); + } + } + if (mcpClosureDiagnostics.length > 0) { + console.error( + `[MCP session-closure diagnostics: ${file}; last ${mcpClosureDiagnostics.length} matching lines]\n${mcpClosureDiagnostics.join("\n")}` + ); + } + } } catch { console.error("SDK diagnostics unavailable"); } @@ -309,6 +338,7 @@ export async function createSdkTestContext({ } await openAiEndpoint.updateConfig({ + backend, replayOnly, filePath: getTrafficCapturePath(testContext), workDir, diff --git a/nodejs/test/e2e/harness/webSocketResponsesServer.ts b/nodejs/test/e2e/harness/webSocketResponsesServer.ts index 219100a97f..de4810802c 100644 --- a/nodejs/test/e2e/harness/webSocketResponsesServer.ts +++ b/nodejs/test/e2e/harness/webSocketResponsesServer.ts @@ -14,6 +14,9 @@ export async function startWebSocketResponsesServer() { messages: [] as Array>, messageConnectionIds: [] as number[], responses: [] as WebSocketReply[], + /** Scripted HTTP `/responses` SSE replies; when empty, HTTP requests are forwarded. */ + httpResponses: [] as WebSocketEnvelope[][], + httpResponseRequestPaths: [] as string[], openConnectionIds: new Set(), connectionCount: 0, closedConnectionCount: 0, @@ -51,6 +54,24 @@ export async function startWebSocketResponsesServer() { ); return; } + const scriptedHttpEvents = + req.method === "POST" && + new URL(req.url ?? "/", "http://localhost").pathname.endsWith("/responses") + ? state.httpResponses.shift() + : undefined; + if (scriptedHttpEvents) { + state.httpResponseRequestPaths.push(req.url ?? "/"); + req.resume(); + req.on("error", (error) => errors.push(error)); + req.on("end", () => { + res.writeHead(200, { "content-type": "text/event-stream" }); + for (const event of scriptedHttpEvents) { + res.write(`event: ${String(event.type)}\ndata: ${JSON.stringify(event)}\n\n`); + } + res.end(); + }); + return; + } if (!httpTargetUrl) { res.writeHead(503); res.end("The test CAPI proxy has not been attached."); diff --git a/nodejs/test/e2e/hydrafusion_max.e2e.test.ts b/nodejs/test/e2e/hydrafusion_max.e2e.test.ts index a4a7c52c93..078dbeeaab 100644 --- a/nodejs/test/e2e/hydrafusion_max.e2e.test.ts +++ b/nodejs/test/e2e/hydrafusion_max.e2e.test.ts @@ -3,8 +3,10 @@ *--------------------------------------------------------------------------------------------*/ import { describe, expect, it } from "vitest"; +import { z } from "zod"; import { approveAll, + defineTool, CopilotRequestHandler, RuntimeConnection, type SessionEvent, @@ -19,9 +21,37 @@ import { isByokBackend } from "./harness/testBackend.js"; const MODEL = "gpt-5.6-sol"; const REPLY = "SDK_HYDRAFUSION_MAX_DONE: Au"; +interface ChatToolCall { + id: string; + type: "function"; + function: { name: string; arguments: string }; +} + +interface ChatMessage { + role: string; + content?: unknown; + tool_calls?: ChatToolCall[]; + tool_call_id?: string; +} + +interface ChatRequest { + model?: string; + stream?: boolean; + messages: ChatMessage[]; +} + class FusionRequestHandler extends CopilotRequestHandler { readonly plans: unknown[] = []; - readonly inference: unknown[] = []; + readonly inference: ChatRequest[] = []; + + constructor( + private readonly reply: (request: ChatRequest) => ChatMessage = () => ({ + role: "assistant", + content: REPLY, + }) + ) { + super(); + } protected override async sendRequest(request: Request): Promise { const path = new URL(request.url).pathname; @@ -57,23 +87,19 @@ class FusionRequestHandler extends CopilotRequestHandler { }); } if (path === "/chat/completions") { - const body = (await request.json()) as { model?: string; stream?: boolean }; + const body = (await request.json()) as ChatRequest; this.inference.push(body); if (body.model !== MODEL) { throw new Error(`Expected concrete Fusion constituent, received ${body.model}`); } + const message = this.reply(body); + const finishReason = message.tool_calls ? "tool_calls" : "stop"; const completion = { id: "chatcmpl-sdk-max", object: "chat.completion", created: 0, model: MODEL, - choices: [ - { - index: 0, - message: { role: "assistant", content: REPLY }, - finish_reason: "stop", - }, - ], + choices: [{ index: 0, message, finish_reason: finishReason }], usage: { prompt_tokens: 11, completion_tokens: 7, total_tokens: 18 }, }; if (!body.stream) return Response.json(completion); @@ -84,8 +110,18 @@ class FusionRequestHandler extends CopilotRequestHandler { choices: [ { index: 0, - delta: { role: "assistant", content: REPLY }, - finish_reason: "stop", + delta: { + ...message, + ...(message.tool_calls + ? { + tool_calls: message.tool_calls.map((call, index) => ({ + index, + ...call, + })), + } + : {}), + }, + finish_reason: finishReason, }, ], })}\n\ndata: [DONE]\n\n`, @@ -264,3 +300,191 @@ describe.skipIf(isByokBackend).each([false, true])( } } ); + +const DRAFT_NOTE = "FUSION_DRAFT_NOTE_BEFORE_STOP"; +const RECORD_CALL_ID = "call_fusion_record_progress"; +const RECORD_RESULT = "FUSION_PROGRESS_RECORDED"; +const WAIT_CALL_ID = "call_fusion_wait_for_stop"; +const FOLLOW_UP_PROMPT = "FUSION_FOLLOW_UP_AFTER_STOP: continue where you left off."; +const FOLLOW_UP_REPLY = "FUSION_FOLLOW_UP_DONE"; + +function messageText(content: unknown): string { + if (typeof content === "string") return content; + if (Array.isArray(content)) { + return content + .map((part) => (typeof part?.text === "string" ? (part.text as string) : "")) + .join(""); + } + return ""; +} + +// The draft writes a note and finishes one tool call, then blocks in a second one until the +// user stops the turn. The follow-up turn answers once it sees the user's next prompt. +function interruptedDraftReply(request: ChatRequest): ChatMessage { + if ( + request.messages.some((message) => messageText(message.content).includes(FOLLOW_UP_PROMPT)) + ) { + return { role: "assistant", content: FOLLOW_UP_REPLY }; + } + const recorded = request.messages.some( + (message) => message.role === "tool" && message.tool_call_id === RECORD_CALL_ID + ); + const call = (id: string, name: string, args: object): ChatToolCall => ({ + id, + type: "function", + function: { name, arguments: JSON.stringify(args) }, + }); + return recorded + ? { role: "assistant", content: "", tool_calls: [call(WAIT_CALL_ID, "wait_for_stop", {})] } + : { + role: "assistant", + content: DRAFT_NOTE, + tool_calls: [call(RECORD_CALL_ID, "record_progress", { note: "alpha" })], + }; +} + +async function withTimeout(promise: Promise, ms: number, label: string): Promise { + let timer: ReturnType | undefined; + try { + return await Promise.race([ + promise, + new Promise((_, reject) => { + timer = setTimeout(() => reject(new Error(`Timeout: ${label}`)), ms); + }), + ]); + } finally { + if (timer) clearTimeout(timer); + } +} + +describe.skipIf(isByokBackend)("HydraFusion interrupted turn", async () => { + const handler = new FusionRequestHandler(interruptedDraftReply); + const { copilotClient: client, workDir } = await createSdkTestContext({ + copilotClientOptions: { + gitHubToken: DEFAULT_GITHUB_TOKEN, + requestHandler: handler, + env: { + HYDRAFUSION: "true", + HYDRAFUSION_ROLLOUT: "true", + HYDRAFUSION_PLAN_V2: "true", + COPILOT_EXP_COPILOT_CLI_HYDRAFUSION_PLAN_V2: "true", + HYDRAFUSION_INTERRUPTED_COMMIT: "true", + }, + }, + }); + + it("keeps the stopped draft's work in history and in the next turn", async () => { + let markWaitStarted!: () => void; + const waitStarted = new Promise((resolve) => { + markWaitStarted = resolve; + }); + let releaseWait!: () => void; + const waitReleased = new Promise((resolve) => { + releaseWait = resolve; + }); + const session = await client.createSession({ + model: "hydrafusion-max", + enableExperimentalMode: true, + workingDirectory: workDir, + onPermissionRequest: approveAll, + tools: [ + defineTool("record_progress", { + description: "Records a progress note.", + parameters: z.object({ note: z.string() }), + handler: async () => RECORD_RESULT, + }), + defineTool("wait_for_stop", { + description: "Blocks until released.", + parameters: z.object({}), + handler: async (_args, invocation) => { + markWaitStarted(); + await new Promise((resolve) => { + void waitReleased.then(resolve); + invocation.signal?.addEventListener("abort", () => resolve(), { + once: true, + }); + }); + return "RELEASED_AFTER_STOP"; + }, + }), + ], + }); + let stopRequested = false; + let markStoppedIdle!: () => void; + const stoppedIdle = new Promise((resolve) => { + markStoppedIdle = resolve; + }); + const unsubscribe = session.on((event) => { + if (stopRequested && event.type === "session.idle") markStoppedIdle(); + }); + try { + void session.send({ prompt: "Record progress, then wait for the stop signal." }); + await withTimeout(waitStarted, 60_000, "wait_for_stop start"); + stopRequested = true; + await session.abort(); + releaseWait(); + await withTimeout(stoppedIdle, 60_000, "idle after stop"); + + const history = await session.getEvents(); + const commit = history.find((event) => event.type === "session.fusion_commit_started"); + expect(commit?.data).toMatchObject({ + commitId: expect.stringMatching(/:commit:interrupted:0$/), + }); + const commitId = (commit?.data as { commitId: string }).commitId; + const committed = history.filter( + (event) => + (event.data as { fusion?: { commitId?: string } }).fusion?.commitId === commitId + ); + expect(committed).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + type: "assistant.message", + data: expect.objectContaining({ content: DRAFT_NOTE }), + }), + expect.objectContaining({ + type: "tool.execution_complete", + data: expect.objectContaining({ + toolCallId: RECORD_CALL_ID, + success: true, + }), + }), + ]) + ); + expect(history.some((event) => event.type === "session.fusion_completed")).toBe(false); + + const reply = await session.sendAndWait({ prompt: FOLLOW_UP_PROMPT }); + expect(reply?.data.content).toBe(FOLLOW_UP_REPLY); + const followUp = handler.inference.find((request) => + request.messages.some((message) => + messageText(message.content).includes(FOLLOW_UP_PROMPT) + ) + ); + const messages = followUp?.messages ?? []; + const followUpIndex = messages.findIndex((message) => + messageText(message.content).includes(FOLLOW_UP_PROMPT) + ); + const earlier = messages.slice(0, followUpIndex); + expect( + earlier.some( + (message) => + message.role === "assistant" && + messageText(message.content).includes(DRAFT_NOTE) + ) + ).toBe(true); + expect( + earlier.flatMap((message) => message.tool_calls?.map((call) => call.id) ?? []) + ).toEqual(expect.arrayContaining([RECORD_CALL_ID, WAIT_CALL_ID])); + const results = new Map( + earlier + .filter((message) => message.role === "tool") + .map((message) => [message.tool_call_id, messageText(message.content)]) + ); + expect(results.get(RECORD_CALL_ID)).toContain(RECORD_RESULT); + expect(results.has(WAIT_CALL_ID)).toBe(true); + } finally { + releaseWait(); + unsubscribe(); + await session.disconnect(); + } + }); +}); diff --git a/nodejs/test/e2e/image_edits.e2e.test.ts b/nodejs/test/e2e/image_edits.e2e.test.ts new file mode 100644 index 0000000000..952a61fb0c --- /dev/null +++ b/nodejs/test/e2e/image_edits.e2e.test.ts @@ -0,0 +1,508 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { createHash, randomUUID } from "node:crypto"; +import { readFile } from "node:fs/promises"; +import { join } from "node:path"; +import { deflateSync } from "node:zlib"; +import { describe, expect, it } from "vitest"; +import { + approveAll, + CopilotRequestHandler, + type CopilotClient, + type CopilotRequestContext, + type SessionEvent, + type UserPromptTransformedHookInput, +} from "../../src/index.js"; +import { createSdkTestContext } from "./harness/sdkTestContext.js"; + +const MODEL = "gpt-image-2.5-sunburst"; +const FINAL = "IMAGE_EDIT_SDK_FINISHED"; + +function png(red: number, width = 1): string { + function chunk(name: string, data: Buffer): Buffer { + const payload = Buffer.concat([Buffer.from(name), data]); + let crc = 0xffffffff; + for (const byte of payload) { + crc ^= byte; + for (let bit = 0; bit < 8; bit++) { + crc = (crc >>> 1) ^ (crc & 1 ? 0xedb88320 : 0); + } + } + const length = Buffer.alloc(4); + length.writeUInt32BE(data.length); + const checksum = Buffer.alloc(4); + checksum.writeUInt32BE((crc ^ 0xffffffff) >>> 0); + return Buffer.concat([length, payload, checksum]); + } + const header = Buffer.alloc(13); + header.writeUInt32BE(width, 0); + header.writeUInt32BE(1, 4); + header[8] = 8; + header[9] = 2; + const pixels = Buffer.alloc(1 + width * 3); + for (let column = 0; column < width; column++) { + pixels[1 + column * 3] = red; + } + return Buffer.concat([ + Buffer.from("89504e470d0a1a0a", "hex"), + chunk("IHDR", header), + chunk("IDAT", deflateSync(pixels)), + chunk("IEND", Buffer.alloc(0)), + ]).toString("base64"); +} + +function imageId(data: string): string { + return `sha256:${createHash("sha256").update(Buffer.from(data, "base64")).digest("hex")}`; +} + +const SOURCE = png(20); +const EDITED = png(200); + +class ImageHandler extends CopilotRequestHandler { + readonly edits: Array> = []; + readonly generations: Array> = []; + readonly chats: Array> = []; + outputImage = EDITED; + supportedEndpoints = ["/v1/images/generations", "/v1/images/edits"]; + nextArguments: Record | undefined; + private calls = 0; + + protected override async sendRequest( + request: Request, + _context: CopilotRequestContext + ): Promise { + const path = new URL(request.url).pathname; + const body: Record = request.body ? await request.json() : {}; + if (path.endsWith("/models")) { + return Response.json({ + data: [ + { + id: MODEL, + name: "Image test", + capabilities: { type: "image" }, + supported_endpoints: this.supportedEndpoints, + model_picker_enabled: false, + policy: { state: "enabled" }, + }, + ], + }); + } + if (path.endsWith("/models/session") || path.includes("/policy")) { + return Response.json({ state: "enabled" }); + } + if (path === "/v1/images/edits" || path === "/v1/images/generations") { + (path.endsWith("/edits") ? this.edits : this.generations).push(body); + return Response.json({ + created: 1, + data: [{ b64_json: this.outputImage }], + output_format: "png", + }); + } + if (path.endsWith("/chat/completions")) { + this.chats.push(body); + const args = this.nextArguments; + this.nextArguments = undefined; + const toolCalls = args + ? [ + { + id: `image-edit-${++this.calls}`, + type: "function", + function: { name: "image_generation", arguments: JSON.stringify(args) }, + }, + ] + : undefined; + const message = { + role: "assistant", + content: args ? null : FINAL, + tool_calls: toolCalls, + }; + const finish = args ? "tool_calls" : "stop"; + if (body.stream === true) { + const delta = { + ...message, + tool_calls: toolCalls?.map((call, index) => ({ index, ...call })), + }; + return new Response( + [ + `data: ${JSON.stringify({ id: "image-chat", object: "chat.completion.chunk", choices: [{ index: 0, delta, finish_reason: null }] })}\n\n`, + `data: ${JSON.stringify({ id: "image-chat", object: "chat.completion.chunk", choices: [{ index: 0, delta: {}, finish_reason: finish }], usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 } })}\n\n`, + "data: [DONE]\n\n", + ].join(""), + { headers: { "content-type": "text/event-stream" } } + ); + } + return Response.json({ + id: "image-chat", + object: "chat.completion", + model: "image-edit-chat", + choices: [{ index: 0, message, finish_reason: finish }], + usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 }, + }); + } + throw new Error(`Unexpected image test request: ${request.method} ${path}`); + } +} + +describe("Native image editing", async () => { + const { createClient, env, openAiEndpoint } = await createSdkTestContext({ + copilotClientOptions: { env: { IMAGE_GENERATION_TOOL: "true" } }, + }); + async function readPersistedAssets(client: CopilotClient, sessionId: string) { + // Binary assets are internal persistence records, not public SDK events. + await client.rpc.sessions.save({ sessionId }); + const log = await readFile( + join(env.COPILOT_HOME, "session-state", sessionId, "events.jsonl"), + "utf8" + ); + return log + .trim() + .split("\n") + .map((line): SessionEvent => JSON.parse(line)) + .filter((event) => event.type === "session.binary_asset"); + } + const options = { + model: "image-edit-chat", + imageGeneration: { enabled: true }, + provider: { + type: "openai" as const, + wireApi: "completions" as const, + baseUrl: "https://image-edit-test.invalid/v1", + apiKey: "synthetic-image-test", + }, + onPermissionRequest: approveAll, + }; + + it("edits an explicit attachment and can reuse the generated result after resume", async () => { + const handler = new ImageHandler(); + const client = createClient({ requestHandler: handler }); + try { + await client.start(); + const session = await client.createSession(options); + const events: SessionEvent[] = []; + session.on((event) => events.push(event)); + handler.nextArguments = { + prompt: "Use this reference", + input_images: [{ image_id: imageId(SOURCE) }], + }; + const reply = await session.sendAndWait({ + prompt: "Use the attached image as a reference for a new image.", + attachments: [{ type: "blob", mimeType: "image/png", data: SOURCE }], + }); + expect(reply?.data.content).toBe(FINAL); + const completion = events.find((event) => event.type === "tool.execution_complete"); + expect(completion?.data.error).toBeUndefined(); + expect(completion?.data.success).toBe(true); + expect(handler.generations).toHaveLength(0); + expect(handler.edits).toEqual([ + { + model: MODEL, + prompt: "Use this reference", + images: [{ image_url: `data:image/png;base64,${SOURCE}` }], + n: 1, + output_format: "png", + stream: false, + }, + ]); + const sessionId = session.sessionId; + await session.disconnect(); + await client.stop(); + await client.start(); + const resumed = await client.resumeSession(sessionId, options); + handler.nextArguments = { + prompt: "Make the result brighter", + input_images: [{ image_id: imageId(EDITED) }], + }; + expect( + (await resumed.sendAndWait({ prompt: "Make the generated image brighter." }))?.data + .content + ).toBe(FINAL); + expect(handler.edits).toHaveLength(2); + expect(handler.edits[1].images).toEqual([ + { image_url: `data:image/png;base64,${EDITED}` }, + ]); + expect(handler.generations).toHaveLength(0); + await resumed.disconnect(); + } finally { + await client.stop(); + } + }); + + it("does not upload an unrelated attachment when the tool selects text-only generation", async () => { + const handler = new ImageHandler(); + const client = createClient({ requestHandler: handler }); + try { + await client.start(); + const session = await client.createSession(options); + handler.nextArguments = { prompt: "A fresh landscape" }; + expect( + ( + await session.sendAndWait({ + prompt: "Ignore the attachment and generate a fresh landscape.", + attachments: [{ type: "blob", mimeType: "image/png", data: SOURCE }], + }) + )?.data.content + ).toBe(FINAL); + expect(handler.edits).toHaveLength(0); + expect(handler.generations).toHaveLength(1); + expect(handler.generations[0]).not.toHaveProperty("images"); + await session.disconnect(); + } finally { + await client.stop(); + } + }); + + it("rejects image references outside the session instead of generating a substitute", async () => { + const handler = new ImageHandler(); + const client = createClient({ requestHandler: handler }); + try { + await client.start(); + const session = await client.createSession(options); + const events: SessionEvent[] = []; + session.on((event) => events.push(event)); + handler.nextArguments = { + prompt: "Edit", + input_images: [{ image_id: imageId(SOURCE) }], + }; + expect( + (await session.sendAndWait({ prompt: "Edit the selected image." }))?.data.content + ).toBe(FINAL); + expect(handler.edits).toHaveLength(0); + expect(handler.generations).toHaveLength(0); + const completion = events.find((event) => event.type === "tool.execution_complete"); + expect(completion?.data.error?.message).toMatch(/^image_generation_input_unavailable:/); + expect(completion?.data.success).toBe(false); + await session.disconnect(); + } finally { + await client.stop(); + } + }); + + it("keeps the transformed hook final for batched image attachments", async () => { + const handler = new ImageHandler(); + const client = createClient({ requestHandler: handler }); + const preceding = "Inspect the first batch image."; + const replacement = "IMAGE_BATCH_HOOK_REPLACEMENT"; + const inputs: UserPromptTransformedHookInput[] = []; + try { + await client.start(); + const session = await client.createSession({ + ...options, + hooks: { + onUserPromptTransformed: async (input) => { + inputs.push(input); + return { modifiedTransformedPrompt: replacement }; + }, + }, + }); + const events: SessionEvent[] = []; + session.on((event) => events.push(event)); + await session.rpc.sendMessages({ + messages: [ + { + prompt: preceding, + attachments: [{ type: "blob", mimeType: "image/png", data: SOURCE }], + }, + { prompt: "Finish the batch." }, + ], + wait: true, + }); + expect(inputs.find((input) => input.prompt === preceding)?.transformedPrompt).toContain( + imageId(SOURCE) + ); + const messages = events.filter((event) => event.type === "user.message"); + expect(messages).toHaveLength(2); + for (const message of messages) { + expect(message.data.transformedContent).toBe(replacement); + } + expect(JSON.stringify(handler.chats.at(-1)?.messages)).not.toContain( + "Image input references" + ); + // Validate the final assistant response arrived (guards against truncated captures). + expect( + events.findLast((event) => event.type === "assistant.message")?.data.content + ).toBe(FINAL); + await session.disconnect(); + } finally { + await client.stop(); + } + }); + + it("emits the preceding image message when its transformed hook throws", async () => { + const handler = new ImageHandler(); + const client = createClient({ requestHandler: handler }); + const preceding = "Inspect the first batch image."; + const hooked: string[] = []; + try { + await client.start(); + const session = await client.createSession({ + ...options, + hooks: { + onUserPromptTransformed: async (input) => { + hooked.push(input.prompt); + if (input.prompt === preceding) { + throw new Error("IMAGE_TRANSFORM_EXPECTED_FAILURE"); + } + }, + }, + }); + const events: SessionEvent[] = []; + session.on((event) => events.push(event)); + await session.rpc.sendMessages({ + messages: [ + { + prompt: preceding, + attachments: [{ type: "blob", mimeType: "image/png", data: SOURCE }], + }, + { prompt: "Finish the batch." }, + ], + wait: true, + }); + expect(hooked).toEqual([preceding, "Finish the batch."]); + const messages = events.filter((event) => event.type === "user.message"); + expect(messages.map((message) => message.data.content)).toEqual(hooked); + expect(messages[0].data.attachments).toContainEqual( + expect.objectContaining({ type: "blob", mimeType: "image/png", data: SOURCE }) + ); + expect( + (await readPersistedAssets(client, session.sessionId)).map( + (asset) => asset.data.assetId + ) + ).toEqual([imageId(SOURCE)]); + // Validate the final assistant response arrived (guards against truncated captures). + expect( + events.findLast((event) => event.type === "assistant.message")?.data.content + ).toBe(FINAL); + await session.disconnect(); + } finally { + await client.stop(); + } + }); + + it("adds a persisted preview when reattaching a generated image", async () => { + const handler = new ImageHandler(); + handler.outputImage = png(200, 2200); + const client = createClient({ requestHandler: handler }); + try { + await client.start(); + const session = await client.createSession(options); + handler.nextArguments = { prompt: "Generate a wide image." }; + expect( + (await session.sendAndWait({ prompt: "Generate a wide image." }))?.data.content + ).toBe(FINAL); + expect(handler.generations).toHaveLength(1); + expect( + ( + await session.sendAndWait({ + prompt: "Inspect the reattached image.", + attachments: [ + { type: "blob", mimeType: "image/png", data: handler.outputImage }, + ], + }) + )?.data.content + ).toBe(FINAL); + const assets = await readPersistedAssets(client, session.sessionId); + const update = assets.findLast( + (event) => event.data.assetId === `image-edit:${imageId(handler.outputImage)}` + ); + expect(update?.data.metadata?.imageEditSource).toEqual({ + data: handler.outputImage, + mimeType: "image/png", + }); + const preview = update?.data.data; + if (typeof preview !== "string") { + throw new Error("The reattached image has no persisted chat preview"); + } + expect(preview).not.toBe(handler.outputImage); + expect(Buffer.from(preview, "base64").length).toBeLessThanOrEqual(3 * 1024 * 1024); + const imageMessage = expect.objectContaining({ + role: "user", + content: expect.arrayContaining([ + expect.objectContaining({ + type: "image_url", + image_url: expect.objectContaining({ + url: `data:image/png;base64,${preview}`, + }), + }), + ]), + }); + expect(handler.chats.at(-1)?.messages).toContainEqual(imageMessage); + + const sessionId = session.sessionId; + await session.disconnect(); + await client.stop(); + await client.start(); + const resumed = await client.resumeSession(sessionId, options); + expect((await resumed.sendAndWait({ prompt: "Inspect it again." }))?.data.content).toBe( + FINAL + ); + expect(handler.chats.at(-1)?.messages).toContainEqual(imageMessage); + handler.nextArguments = { + prompt: "Edit the original wide image.", + input_images: [{ image_id: imageId(handler.outputImage) }], + }; + // Validate the final assistant response arrived (guards against truncated captures). + expect( + (await resumed.sendAndWait({ prompt: "Edit the reattached image." }))?.data.content + ).toBe(FINAL); + expect(handler.edits.at(-1)?.images).toEqual([ + { image_url: `data:image/png;base64,${handler.outputImage}` }, + ]); + await resumed.disconnect(); + } finally { + await client.stop(); + } + }); + + it.each(["generations-only", "filtered-out"] as const)( + "does not retain originals or advertise image inputs when %s", + async (availability) => { + const handler = new ImageHandler(); + if (availability === "generations-only") { + handler.supportedEndpoints = ["/v1/images/generations"]; + } + // In-process clients share the model catalog cache, keyed by endpoint and auth. + const token = `image-input-${randomUUID()}`; + await openAiEndpoint.setCopilotUserByToken(token, { + login: "image-input-test", + copilot_plan: "individual_pro", + endpoints: { api: env.COPILOT_API_URL }, + analytics_tracking_id: token, + }); + const client = createClient({ requestHandler: handler, gitHubToken: token }); + const inputs: UserPromptTransformedHookInput[] = []; + try { + await client.start(); + const session = await client.createSession({ + ...options, + excludedTools: availability === "filtered-out" ? ["image_generation"] : [], + hooks: { + onUserPromptTransformed: async (input) => { + inputs.push(input); + }, + }, + }); + const original = png(20, 2200); + // Validate the final assistant response arrived (guards against truncated captures). + expect( + ( + await session.sendAndWait({ + prompt: "Inspect this attachment.", + attachments: [{ type: "blob", mimeType: "image/png", data: original }], + }) + )?.data.content + ).toBe(FINAL); + expect(inputs).toHaveLength(1); + expect(inputs[0].transformedPrompt).not.toContain("Image input references"); + const assets = await readPersistedAssets(client, session.sessionId); + expect(assets).toHaveLength(1); + expect(assets[0].data.data).not.toBe(original); + expect(assets[0].data.metadata?.imageEditSource).toBeUndefined(); + await session.disconnect(); + } finally { + await client.stop(); + } + } + ); +}); diff --git a/nodejs/test/e2e/image_generation.e2e.test.ts b/nodejs/test/e2e/image_generation.e2e.test.ts new file mode 100644 index 0000000000..2473d39127 --- /dev/null +++ b/nodejs/test/e2e/image_generation.e2e.test.ts @@ -0,0 +1,254 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { describe, expect, it } from "vitest"; +import { + approveAll, + CopilotRequestHandler, + RuntimeConnection, + type CopilotRequestContext, + type CopilotSession, +} from "../../src/index.js"; +import { createSdkTestContext } from "./harness/sdkTestContext.js"; +import { withTestBackend } from "./harness/testBackend.js"; + +const IMAGE_MODEL = "gpt-image-2.5-sunburst"; +const IMAGE_ENDPOINT = "/v1/images/generations"; +const PNG = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAABHNCSVQICAgIfAhkiAAAAAFzUkdCAK7OHOkAAAANSURBVAiZY2BgYPgPAAEEAQB9ssjfAAAAAElFTkSuQmCC"; + +class ImageService extends CopilotRequestHandler { + readonly imageRequests: unknown[] = []; + readonly chatToolNames: string[][] = []; + + constructor(private readonly modelAvailable = true) { + super(); + } + + protected override async sendRequest( + request: Request, + context: CopilotRequestContext + ): Promise { + const pathname = new URL(request.url).pathname; + if (pathname.endsWith("/chat/completions") && request.method === "POST") { + const body = (await request.json()) as { + model: string; + stream?: boolean; + tools?: Array<{ function: { name: string } }>; + }; + this.chatToolNames.push(body.tools?.map((tool) => tool.function.name) ?? []); + const message = { role: "assistant", content: "IMAGE_CONSENT_TURN_COMPLETE" }; + const response = { + id: "image-consent-turn", + created: 123, + model: body.model, + usage: { prompt_tokens: 5, completion_tokens: 1, total_tokens: 6 }, + }; + if (!body.stream) { + return Response.json({ + ...response, + object: "chat.completion", + choices: [{ index: 0, message, finish_reason: "stop" }], + }); + } + return new Response( + `data: ${JSON.stringify({ + ...response, + object: "chat.completion.chunk", + choices: [{ index: 0, delta: message, finish_reason: "stop" }], + })}\n\ndata: [DONE]\n\n`, + { headers: { "content-type": "text/event-stream" } } + ); + } + if (pathname === IMAGE_ENDPOINT) { + this.imageRequests.push(await request.json()); + return Response.json({ + created: 123, + data: [{ b64_json: PNG }], + output_format: "png", + }); + } + const response = await super.sendRequest(request, context); + if (pathname !== "/models" || !response.ok) { + return response; + } + const catalog = (await response.json()) as { data: unknown[] }; + if (this.modelAvailable) { + catalog.data.push({ + id: IMAGE_MODEL, + name: "SDK test image model", + capabilities: { type: "image" }, + supported_endpoints: [IMAGE_ENDPOINT], + model_picker_enabled: false, + policy: { state: "enabled" }, + }); + } + return Response.json(catalog); + } +} + +async function imageToolOffered(session: CopilotSession): Promise { + await session.rpc.tools.initializeAndValidate(); + const { tools } = await session.rpc.tools.getCurrentMetadata(); + return tools.some((tool) => tool.name === "image_generation"); +} + +describe("Native image generation consent", async () => { + // Native image inference is CAPI-only, independent of the chat-provider CI matrix. + const { createClient, openAiEndpoint } = await createSdkTestContext({ + replayOnly: true, + backend: "capi", + }); + + it("requires SDK consent and CAPI access regardless of the CLI rollout flag", async () => { + for (const flag of [false, true]) { + for (const enabled of [undefined, false, true]) { + const service = new ImageService(); + await using client = createClient({ requestHandler: service }); + await using session = await client.createSession({ + onPermissionRequest: approveAll, + featureFlags: { IMAGE_GENERATION_TOOL: flag }, + imageGeneration: enabled === undefined ? undefined : { enabled }, + }); + expect(await imageToolOffered(session)).toBe(enabled === true); + expect(service.imageRequests).toEqual([]); + } + } + }); + + it("executes an opted-in CAPI image tool while the rollout flag is off", async () => { + const service = new ImageService(); + await using client = createClient({ requestHandler: service }); + await using session = await client.createSession({ + onPermissionRequest: approveAll, + featureFlags: { IMAGE_GENERATION_TOOL: false }, + imageGeneration: { enabled: true }, + }); + expect(await imageToolOffered(session)).toBe(true); + const result = await session.rpc.tools.execute({ + name: "image_generation", + arguments: { prompt: "A watercolor fox" }, + toolCallId: "sdk-image", + }); + expect(result).toMatchObject({ + resultType: "success", + contents: [{ type: "image", mimeType: "image/png", data: PNG }], + }); + expect(service.imageRequests).toEqual([ + { + model: IMAGE_MODEL, + prompt: "A watercolor fox", + n: 1, + output_format: "png", + stream: false, + }, + ]); + }); + + it("still requires an image model from CAPI", async () => { + const service = new ImageService(false); + await using client = createClient({ requestHandler: service }); + await using session = await client.createSession({ + onPermissionRequest: approveAll, + imageGeneration: { enabled: true }, + featureFlags: { IMAGE_GENERATION_TOOL: true }, + }); + expect(await imageToolOffered(session)).toBe(false); + expect(service.imageRequests).toEqual([]); + }); + + it("preserves resident consent but applies an explicit opt-out", async () => { + await using client = createClient({ requestHandler: new ImageService() }); + await using session = await client.createSession({ + onPermissionRequest: approveAll, + imageGeneration: { enabled: true }, + featureFlags: { IMAGE_GENERATION_TOOL: false }, + }); + expect(await imageToolOffered(session)).toBe(true); + await using resumed = await client.resumeSession(session.sessionId, { + onPermissionRequest: approveAll, + }); + expect(await imageToolOffered(resumed)).toBe(true); + await using disabled = await client.resumeSession(session.sessionId, { + onPermissionRequest: approveAll, + imageGeneration: { enabled: false }, + featureFlags: { IMAGE_GENERATION_TOOL: true }, + }); + expect(await imageToolOffered(disabled)).toBe(false); + }); + + it("requires renewed consent after a runtime restart", async () => { + // Separate stdio runtimes prove that consent is not restored from disk. + const firstService = new ImageService(); + await using first = createClient({ + requestHandler: firstService, + connection: RuntimeConnection.forStdio(), + }); + const original = await first.createSession({ + model: "claude-sonnet-5", + onPermissionRequest: approveAll, + imageGeneration: { enabled: true }, + }); + expect(await imageToolOffered(original)).toBe(true); + // A session with no user activity is deliberately not persisted. + const response = await original.sendAndWait({ + prompt: "Acknowledge this message without using tools.", + }); + expect(response?.data.content).toBe("IMAGE_CONSENT_TURN_COMPLETE"); + expect(firstService.chatToolNames.some((names) => names.includes("image_generation"))).toBe( + true + ); + const sessionId = original.sessionId; + await first.rpc.sessions.save({ sessionId }); + await original.disconnect(); + await first.stop(); + + const secondService = new ImageService(); + await using second = createClient({ + requestHandler: secondService, + connection: RuntimeConnection.forStdio(), + }); + await using resumed = await second.resumeSession(sessionId, { + onPermissionRequest: approveAll, + featureFlags: { IMAGE_GENERATION_TOOL: true }, + }); + expect(await imageToolOffered(resumed)).toBe(false); + const resumedResponse = await resumed.sendAndWait({ + prompt: "Acknowledge again without using tools.", + }); + expect(resumedResponse?.data.content).toBe("IMAGE_CONSENT_TURN_COMPLETE"); + expect(secondService.chatToolNames.length).toBeGreaterThan(0); + expect( + secondService.chatToolNames.every((names) => !names.includes("image_generation")) + ).toBe(true); + await using optedIn = await second.resumeSession(sessionId, { + onPermissionRequest: approveAll, + imageGeneration: { enabled: true }, + featureFlags: { IMAGE_GENERATION_TOOL: false }, + }); + expect(await imageToolOffered(optedIn)).toBe(true); + }); + + it.each(["anthropic-messages", "openai-responses", "openai-completions"] as const)( + "does not expose image generation for %s without CAPI image-model access", + async (backend) => { + const service = new ImageService(false); + await using client = createClient({ requestHandler: service }); + await using session = await client.createSession( + withTestBackend( + { + model: "claude-sonnet-5", + onPermissionRequest: approveAll, + imageGeneration: { enabled: true }, + featureFlags: { IMAGE_GENERATION_TOOL: true }, + }, + backend, + openAiEndpoint.url + ) + ); + expect(await imageToolOffered(session)).toBe(false); + expect(service.imageRequests).toEqual([]); + } + ); +}); diff --git a/nodejs/test/e2e/managed_permissions_limit_to.e2e.test.ts b/nodejs/test/e2e/managed_permissions_limit_to.e2e.test.ts index 1d02686699..1ec520429d 100644 --- a/nodejs/test/e2e/managed_permissions_limit_to.e2e.test.ts +++ b/nodejs/test/e2e/managed_permissions_limit_to.e2e.test.ts @@ -18,6 +18,123 @@ function closeServer(server: Server): Promise { describe("Managed permissions limitTo", async () => { const { copilotClient: client, workDir } = await createSdkTestContext(); + it("evaluates retained managed network policy without invoking the permission handler", async () => { + let permissionRequests = 0; + const session = await client.createSession({ + managedSettings: { + permissions: { + limitTo: ["Domain(*.example.com)"], + deny: ["Domain(blocked.example.com)"], + ask: ["Domain(prompt.example.com)"], + allow: ["Domain(allowed.example.com)"], + }, + }, + onPermissionRequest: () => { + permissionRequests++; + return { kind: "approve-once" }; + }, + }); + try { + const urls = [ + "https://allowed.example.com/guide", + "https://prompt.example.com/", + "https://blocked.example.com/", + "https://outside.test/", + "http://allowed.example.com:8080/redirect", + ]; + const snapshot = await session.rpc.managedSettings.get(); + expect(snapshot.permissionsContext).toBeDefined(); + const context = snapshot.permissionsContext!; + const operations = urls.map((url) => ({ kind: "url" as const, url })); + const result = await client.rpc.managedSettings.permissions.evaluate({ + context, + operations, + }); + expect(result).toEqual({ + failClosed: false, + results: operations.map((operation, index) => ({ + operation, + verdict: ["allow", "ask", "deny", "deny", "ask"][index], + })), + }); + expect(permissionRequests).toBe(0); + await expect( + client.rpc.managedSettings.permissions.evaluate({ + context, + operations: [{ kind: "url", url: "file:///secret" }], + }) + ).rejects.toMatchObject({ code: -32602 }); + expect( + await client.rpc.managedSettings.permissions.evaluate({ context, operations: [] }) + ).toEqual({ + failClosed: false, + results: [], + }); + expect(snapshot.settings).not.toHaveProperty("sandbox"); + } finally { + await session.disconnect(); + } + }); + + it("evaluates an in-memory permissions context without creating a session", async () => { + await client.start(); + const operations = [ + { kind: "url" as const, url: "https://api.example.com/guide" }, + { kind: "url" as const, url: "https://outside.test/" }, + ]; + const context = { + permissions: { limitTo: ["Domain(api.example.com)"] }, + failClosed: false, + }; + const result = await client.rpc.managedSettings.permissions.evaluate({ + context, + operations, + }); + expect(result).toEqual({ + failClosed: false, + results: [ + { operation: operations[0], verdict: "unmanaged" }, + { operation: operations[1], verdict: "deny" }, + ], + }); + expect( + await client.rpc.managedSettings.permissions.evaluate({ + context: { ...context, failClosed: true }, + operations, + }) + ).toEqual({ + failClosed: true, + results: operations.map((operation) => ({ operation, verdict: "deny" })), + }); + }); + + it("denies every URL for an empty managed boundary", async () => { + const session = await client.createSession({ + managedSettings: { permissions: { limitTo: [] } }, + }); + try { + const snapshot = await session.rpc.managedSettings.get(); + expect(snapshot.permissionsContext).toBeDefined(); + expect( + await client.rpc.managedSettings.permissions.evaluate({ + context: snapshot.permissionsContext!, + operations: [ + { kind: "url", url: "https://example.com/" }, + { kind: "url", url: "https://api.github.com/" }, + ], + }) + ).toEqual({ + failClosed: false, + results: [ + { operation: { kind: "url", url: "https://example.com/" }, verdict: "deny" }, + { operation: { kind: "url", url: "https://api.github.com/" }, verdict: "deny" }, + ], + }); + } finally { + await session.disconnect(); + } + }); + it.skipIf(process.platform !== "darwin")( "admits only the configured dynamic loopback destination", async () => { diff --git a/nodejs/test/e2e/mcp_and_agents.e2e.test.ts b/nodejs/test/e2e/mcp_and_agents.e2e.test.ts index 414e760ea2..443d5699f0 100644 --- a/nodejs/test/e2e/mcp_and_agents.e2e.test.ts +++ b/nodejs/test/e2e/mcp_and_agents.e2e.test.ts @@ -2,9 +2,11 @@ * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ -import { dirname, resolve } from "path"; +import { createHash } from "node:crypto"; +import { readFile } from "node:fs/promises"; +import { dirname, join, resolve } from "path"; import { fileURLToPath } from "url"; -import { describe, expect, it } from "vitest"; +import { describe, expect, it, onTestFinished } from "vitest"; import { z } from "zod"; import type { CopilotSession, @@ -12,7 +14,7 @@ import type { MCPStdioServerConfig, MCPServerConfig, } from "../../src/index.js"; -import { approveAll, defineTool } from "../../src/index.js"; +import { approveAll, defineTool, RuntimeConnection } from "../../src/index.js"; import { createSdkTestContext } from "./harness/sdkTestContext.js"; const __filename = fileURLToPath(import.meta.url); @@ -57,7 +59,12 @@ async function waitForMcpServerStatus( } describe("MCP Servers and Custom Agents", async () => { - const { copilotClient: client, openAiEndpoint } = await createSdkTestContext(); + const { + copilotClient: client, + openAiEndpoint, + createClient, + workDir, + } = await createSdkTestContext(); describe("MCP Servers", () => { it("should accept MCP server configuration on session create", async () => { @@ -133,6 +140,18 @@ describe("MCP Servers and Custom Agents", async () => { }); it("should pass literal env values to MCP server subprocess", async () => { + const telemetryPath = join(workDir, "mcp-telemetry.jsonl"); + const telemetryClient = createClient({ + connection: RuntimeConnection.forStdio(), + telemetry: { + exporterType: "file", + filePath: telemetryPath, + captureContent: true, + }, + }); + onTestFinished(async () => { + expect(await telemetryClient.stop()).toEqual([]); + }); const mcpServers: Record = { "env-echo": { type: "local", @@ -144,20 +163,50 @@ describe("MCP Servers and Custom Agents", async () => { } as MCPStdioServerConfig, }; - const session = await client.createSession({ + const session = await telemetryClient.createSession({ mcpServers, onPermissionRequest: approveAll, }); - - expect(session.sessionId).toBeDefined(); - await waitForMcpServerStatus(session, "env-echo"); - - const message = await session.sendAndWait({ - prompt: "Use the env-echo/get_env tool to read the TEST_SECRET environment variable. Reply with just the value, nothing else.", - }); - expect(message?.data.content).toContain("hunter2"); - - await session.disconnect(); + try { + expect(session.sessionId).toBeDefined(); + await waitForMcpServerStatus(session, "env-echo"); + + const message = await session.sendAndWait({ + prompt: "Use the env-echo/get_env tool to read the TEST_SECRET environment variable. Reply with just the value, nothing else.", + }); + expect(message?.data.content).toContain("hunter2"); + } finally { + await session.disconnect(); + } + expect(await telemetryClient.stop()).toEqual([]); + + const entries: Array<{ type: string; attributes?: Record }> = ( + await readFile(telemetryPath, "utf8") + ) + .split(/\r?\n/) + .filter((line) => line.trim()) + .map((line) => JSON.parse(line)); + const mcpSpans = entries.filter( + (entry) => + entry.type === "span" && + entry.attributes?.["gen_ai.operation.name"] === "execute_tool" && + typeof entry.attributes[ + "github.copilot.tool.parameters.mcp_server_name_hash" + ] === "string" + ); + expect(mcpSpans.length).toBeGreaterThan(0); + for (const span of mcpSpans) { + expect(span.attributes).toMatchObject({ + "gen_ai.tool.type": "function", + "github.copilot.tool.parameters.mcp_server_name_hash": createHash("sha256") + .update("env-echo") + .digest("hex"), + "github.copilot.tool.parameters.mcp_server_name": "env-echo", + "github.copilot.tool.parameters.mcp_tool_name": "get_env", + "github.copilot.mcp.server.transport": "stdio", + "github.copilot.mcp.server.source": "user", + }); + } }); }); diff --git a/nodejs/test/e2e/mcp_remote_session_closure.e2e.test.ts b/nodejs/test/e2e/mcp_remote_session_closure.e2e.test.ts index 4b6ebf174a..1878998d25 100644 --- a/nodejs/test/e2e/mcp_remote_session_closure.e2e.test.ts +++ b/nodejs/test/e2e/mcp_remote_session_closure.e2e.test.ts @@ -6,7 +6,7 @@ import { spawn } from "node:child_process"; import { dirname, resolve } from "node:path"; import { createInterface } from "node:readline"; import { fileURLToPath } from "node:url"; -import { describe, expect, it, onTestFinished } from "vitest"; +import { describe, expect, it, onTestFailed, onTestFinished } from "vitest"; import type { CopilotSession, MCPServerConfig } from "../../src/index.js"; import { approveAll } from "../../src/index.js"; import { createSdkTestContext } from "./harness/sdkTestContext.js"; @@ -29,10 +29,19 @@ interface McpServerStats { expiredRequestLog: { method: string; elapsedMs: number }[]; protocolVersions: string[]; expiredSessionProbes: number; + recentRequests: Array<{ + elapsedMs: number; + httpMethod: string; + sessionAttached: boolean; + expiredSession: boolean; + rpcMethods: string[]; + status: number | null; + }>; } describe("MCP remote session closure", async () => { const { copilotClient: client } = await createSdkTestContext({ + logLevel: "debug", copilotClientOptions: { env: { COPILOT_MCP_APPS: "true", @@ -46,6 +55,10 @@ describe("MCP remote session closure", async () => { { timeout: 180_000 }, async () => { const remoteServer = await startSessionExpiryMcpServer(); + let lastStats = await remoteServer.stats(); + onTestFailed(() => { + console.error("Last MCP session-expiry observation:", JSON.stringify(lastStats)); + }); const serverName = "remote-server"; const session = await client.createSession({ onPermissionRequest: approveAll, @@ -73,8 +86,8 @@ describe("MCP remote session closure", async () => { await waitForCondition( async () => { - const stats = await remoteServer.stats(); - return stats.expiredSessionProbes >= 1 || stats.initializations >= 2; + lastStats = await remoteServer.stats(); + return lastStats.expiredSessionProbes >= 1 || lastStats.initializations >= 2; }, { timeoutMs: 120_000, @@ -83,7 +96,6 @@ describe("MCP remote session closure", async () => { "remote MCP session expiry was neither probed nor re-initialized", } ); - let lastStats: McpServerStats | undefined; try { await waitForCondition( async () => { @@ -124,20 +136,20 @@ async function waitForMcpServerStatus( serverName: string, expectedStatus: string ): Promise { - let lastStatus = ""; - await waitForCondition( - async () => { - const result = await session.rpc.mcp.list(); - const server = result.servers.find((entry) => entry.name === serverName); - lastStatus = server?.status ?? ""; - return server?.status === expectedStatus; - }, - { - timeoutMs: 120_000, - intervalMs: 200, - timeoutMessage: `${serverName} did not reach ${expectedStatus}; last status was ${lastStatus}`, - } - ); + await expect + .poll( + async () => { + const result = await session.rpc.mcp.list(); + const server = result.servers.find((entry) => entry.name === serverName); + return server?.status ?? ""; + }, + { + timeout: 120_000, + interval: 200, + message: `${serverName} did not reach ${expectedStatus}`, + } + ) + .toBe(expectedStatus); } async function disconnectSession(session: CopilotSession): Promise { @@ -160,6 +172,9 @@ async function startSessionExpiryMcpServer(): Promise<{ const stderr: string[] = []; child.stderr.on("data", (chunk) => stderr.push(String(chunk))); + onTestFailed(() => { + console.error("Session-expiry server stderr:", stderr.join("").slice(-16_384)); + }); const url = await new Promise((resolvePromise, reject) => { const rl = createInterface({ input: child.stdout }); diff --git a/nodejs/test/e2e/mcp_scoped_sign_in.e2e.test.ts b/nodejs/test/e2e/mcp_scoped_sign_in.e2e.test.ts index 4139e17bfe..d067c2bcf2 100644 --- a/nodejs/test/e2e/mcp_scoped_sign_in.e2e.test.ts +++ b/nodejs/test/e2e/mcp_scoped_sign_in.e2e.test.ts @@ -104,6 +104,7 @@ describe("MCP sign-in for one server", async () => { const events: SessionEvent[] = []; const session = await client.createSession({ onPermissionRequest: approveAll, + onEvent: (event) => events.push(event), mcpServers: { alpha: { type: "local", @@ -122,16 +123,42 @@ describe("MCP sign-in for one server", async () => { }, }); onTestFinished(() => disconnectSession(session)); - session.on((event) => events.push(event)); - await waitForMcpServerStatus(session, "alpha", "connected"); await waitForMcpServerStatus(session, "bravo", "needs-auth"); await waitForMcpServerStatus(session, "delta", "needs-auth"); + // Live status can precede delivery of its initial notification. + await waitForCondition( + () => + events.some( + (event) => + event.type === "session.mcp_server_status_changed" && + event.data.serverName === "alpha" && + event.data.status === "connected" + ), + { + timeoutMs: STEP_TIMEOUT_MS, + timeoutMessage: "alpha did not deliver its initial connected status", + } + ); const deltaLogin = await session.rpc.mcp.oauth.login({ serverName: "delta", redirectUri: REDIRECT_URI, }); expect(deltaLogin.authorizationId).toBeDefined(); + // Live status can precede its asynchronous event, so observe startup before measuring peer changes. + await waitForCondition( + () => + events.some( + (event) => + event.type === "session.mcp_server_status_changed" && + event.data.serverName === "alpha" && + event.data.status === "connected" + ), + { + timeoutMs: STEP_TIMEOUT_MS, + timeoutMessage: "alpha's initial connected event did not arrive", + } + ); const baseline = events.length; return { diff --git a/nodejs/test/e2e/memory_tool_overrides.e2e.test.ts b/nodejs/test/e2e/memory_tool_overrides.e2e.test.ts new file mode 100644 index 0000000000..9bec5aad92 --- /dev/null +++ b/nodejs/test/e2e/memory_tool_overrides.e2e.test.ts @@ -0,0 +1,102 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { describe, expect, it } from "vitest"; +import { z } from "zod"; +import { defineTool } from "../../src/index.js"; +import type { PermissionRequest, SessionConfig, ToolInvocation } from "../../src/index.js"; +import { createSdkTestContext } from "./harness/sdkTestContext.js"; + +describe("Memory tool overrides", async () => { + const { copilotClient: client } = await createSdkTestContext({ replayOnly: true }); + + it.each(["store_memory", "vote_memory"])( + "routes %s to the host with custom-tool permissions on create and resume", + async (name) => { + const invocations: ToolInvocation[] = []; + const permissions: PermissionRequest[] = []; + let approve = true; + const config = { + memory: { enabled: false }, + onPermissionRequest: (request) => { + permissions.push(request); + return approve ? { kind: "approve-once" } : { kind: "reject" }; + }, + tools: [ + defineTool(name, { + description: "Host-owned memory", + parameters: z.object({ fact: z.string() }), + overridesBuiltInTool: true, + defer: "never", + handler: (_args, invocation) => { + invocations.push(invocation); + return "HOST_MEMORY_HANDLED"; + }, + }), + ], + } satisfies SessionConfig; + let session = await client.createSession(config); + try { + for (const phase of ["create", "resume"]) { + if (phase === "resume") { + const sessionId = session.sessionId; + await session.disconnect(); + session = await client.resumeSession(sessionId, config); + } + await session.rpc.tools.initializeAndValidate(); + const { tools } = await session.rpc.tools.getCurrentMetadata(); + expect(tools.filter((tool) => tool.name === name)).toEqual([ + expect.objectContaining({ + description: "Host-owned memory", + input_schema: expect.objectContaining({ required: ["fact"] }), + }), + ]); + const args = { fact: `Host-owned memory after ${phase}.` }; + const toolCallId = `memory-override-${phase}`; + const result = await session.rpc.tools.execute({ + name, + arguments: args, + toolCallId, + }); + + expect(result, `${name} after ${phase}`).toMatchObject({ + resultType: "success", + textResultForLlm: "HOST_MEMORY_HANDLED", + }); + expect(invocations.at(-1)).toMatchObject({ + toolName: name, + toolCallId, + arguments: args, + }); + expect(permissions.at(-1)).toMatchObject({ + kind: "custom-tool", + toolName: name, + toolCallId, + args, + }); + } + expect(invocations).toHaveLength(2); + expect(permissions).toHaveLength(2); + + approve = false; + const args = { fact: "A rejected memory." }; + const rejected = await session.rpc.tools.execute({ + name, + arguments: args, + toolCallId: "memory-override-rejected", + }); + expect(rejected.resultType).toBe("rejected"); + expect(invocations).toHaveLength(2); + expect(permissions).toHaveLength(3); + expect(permissions.at(-1)).toMatchObject({ + kind: "custom-tool", + toolName: name, + args, + }); + } finally { + await session.disconnect(); + } + } + ); +}); diff --git a/nodejs/test/e2e/monthly_usage.e2e.test.ts b/nodejs/test/e2e/monthly_usage.e2e.test.ts new file mode 100644 index 0000000000..b0704efa33 --- /dev/null +++ b/nodejs/test/e2e/monthly_usage.e2e.test.ts @@ -0,0 +1,229 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { createServer } from "node:http"; +import { mkdir, writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import { expect, it, onTestFinished } from "vitest"; +import { approveAll, CopilotClient, RuntimeConnection } from "../../src/index.js"; +import { createSdkTestContext } from "./harness/sdkTestContext.js"; + +const { env, workDir, openAiEndpoint } = await createSdkTestContext({ + useStdio: true, + replayOnly: true, + copilotClientOptions: { useLoggedInUser: false, gitHubToken: undefined }, +}); + +it("preserves monthly Microsoft 365 usage without treating it as session spend", async ({ + signal, +}) => { + const profile = join(workDir, "monthly-usage-profile"); + await mkdir(profile); + let usage: Record = {}; + let quotaStatus = 200; + let invalidQuota = false; + const requests: string[] = []; + const server = createServer((request, response) => { + requests.push(`${request.method} ${request.url}`); + expect(request.headers.authorization).toBe("Bearer monthly-usage-token"); + response.setHeader("Content-Type", "application/json"); + if (request.url === "/models") { + response.end( + JSON.stringify({ + data: [{ id: "gpt-5-mini", name: "GPT-5 mini", model_picker_enabled: true }], + }) + ); + } else if (request.url === "/quota") { + if (quotaStatus !== 200) { + response + .writeHead(quotaStatus) + .end(JSON.stringify({ error: { code: "quota_read_failed" } })); + return; + } + if (invalidQuota) { + response.end("not JSON"); + return; + } + response.end( + JSON.stringify({ + quotaSnapshots: { + enterprise: { + source: "enterprise", + service: "GitHubCopilotApp", + accessState: "allowed", + capacityState: "available", + hasQuota: true, + entitledQuantity: 100, + availableQuantity: 13, + ...usage, + }, + }, + }) + ); + } else { + response.writeHead(404).end(); + } + }); + let client: CopilotClient | undefined; + const listening = new Promise((resolve, reject) => { + server.once("error", reject); + server.listen(0, "127.0.0.1", resolve); + }); + onTestFinished(async () => { + try { + await client?.stop(); + } finally { + await listening; + server.closeAllConnections(); + await new Promise((resolve, reject) => + server.close((error) => (error ? reject(error) : resolve())) + ); + } + }); + await listening; + signal.throwIfAborted(); + const address = server.address(); + if (!address || typeof address === "string") throw new Error("Expected a TCP quota fixture"); + const host = `http://127.0.0.1:${address.port}`; + const baseId = "monthly-usage-base"; + const emuToken = "ghu_monthly_usage_emu"; + const account = { host, login: "monthly-user", kind: "loki", derivedFrom: baseId }; + await openAiEndpoint.setCopilotUserByToken(emuToken, { + login: "monthly_emu", + is_staff: true, + copilot_plan: "business", + endpoints: { api: env.COPILOT_API_URL, telemetry: "https://localhost:1/telemetry" }, + }); + await writeFile(join(profile, "settings.json"), JSON.stringify({ storeTokenPlaintext: true })); + await writeFile( + join(profile, "config.json"), + JSON.stringify({ + loggedInUsers: [ + account, + { + host: "https://github.com", + login: "monthly_emu", + kind: "entraEmu", + derivedFrom: baseId, + }, + ], + lastLoggedInUser: account, + authTokens: { + [`entra:${baseId}:entra`]: { + token: JSON.stringify({ hostOwned: { account_id: baseId } }), + }, + [`loki:${baseId}:loki`]: { + token: "monthly-usage-token", + expiresAt: "2099-01-01T00:00:00Z", + accountType: "entra", + }, + "https://github.com:monthly_emu:entra": { + token: emuToken, + expiresAt: "2099-01-01T00:00:00Z", + accountType: "entra", + }, + }, + }) + ); + client = new CopilotClient({ + workingDirectory: workDir, + useLoggedInUser: true, + connection: RuntimeConnection.forStdio({ path: process.env.COPILOT_CLI_PATH }), + env: { + ...env, + COPILOT_HOME: profile, + COPILOT_DISABLE_KEYTAR: "1", + GH_TOKEN: "", + GITHUB_TOKEN: "", + COPILOT_GITHUB_TOKEN: "", + COPILOT_SDK_AUTH_TOKEN: "", + GITHUB_COPILOT_API_TOKEN: "", + COPILOT_HMAC_KEY: "", + CAPI_HMAC_KEY: "", + }, + }); + await using session = await client.createSession({ + configDirectory: profile, + onPermissionRequest: approveAll, + }); + const before = await session.rpc.usage.getMetrics(); + if (process.platform === "linux") { + expect(await session.rpc.gitHubAuth.getStatus()).toMatchObject({ isAuthenticated: false }); + await expect(session.rpc.quota.refresh()).rejects.toThrow("Not authenticated"); + expect((await session.rpc.quota.get()).providerQuotas).toBeUndefined(); + expect((await session.rpc.usage.getMetrics()).totalNanoAiu).toBe(before.totalNanoAiu); + expect(requests).toEqual([]); + return; + } + const initial = await session.rpc.quota.refresh(); + expect( + initial.providerQuotas?.find((state) => state.provider.kind === "loki")?.monthlyUsage + ).toBeUndefined(); + + for (const consumedQuantity of [0, 125.375]) { + usage = { + usageState: "available", + usageUnit: "ai_credits", + usageScope: "user", + consumedQuantity, + usageQueriedAt: "2026-10-08T12:00:00Z", + usageCycleStart: "2026-10-01T00:00:00Z", + usageResetOn: "2026-11-01T00:00:00Z", + }; + const quota = await session.rpc.quota.refresh(); + const state = quota.providerQuotas?.find((state) => state.provider.kind === "loki"); + expect(state).toMatchObject({ + accessState: "allowed", + capacityState: "available", + availableQuantity: 13, + quantityKind: "advisory_balance", + monthlyUsage: { + state: "available", + unit: "ai_credits", + scope: "user", + consumedQuantity, + queriedAt: "2026-10-08T12:00:00Z", + cycleStart: "2026-10-01T00:00:00Z", + resetOn: "2026-11-01T00:00:00Z", + }, + }); + expect((await session.rpc.quota.get()).providerQuotas).toEqual(quota.providerQuotas); + expect(await session.rpc.quota.takeWarnings()).toEqual([]); + } + for (const usageState of ["no_policy", "unavailable"]) { + usage = { usageState, usageUnit: "ai_credits", usageScope: "user" }; + const quota = await session.rpc.quota.refresh(); + expect( + quota.providerQuotas?.find((state) => state.provider.kind === "loki")?.monthlyUsage + ).toEqual({ + state: usageState, + unit: "ai_credits", + scope: "user", + }); + } + for (const status of [401, 403, 404, 429, 503]) { + quotaStatus = status; + const quota = await session.rpc.quota.refresh(); + const state = quota.providerQuotas?.find((state) => state.provider.kind === "loki"); + expect(state).toMatchObject({ + httpStatus: status, + acquisitionStatus: status === 404 ? "unavailable" : "failed", + }); + expect(state?.monthlyUsage).toBeUndefined(); + } + quotaStatus = 200; + invalidQuota = true; + const invalid = await session.rpc.quota.refresh(); + const invalidState = invalid.providerQuotas?.find((state) => state.provider.kind === "loki"); + expect(invalidState).toMatchObject({ + acquisitionStatus: "failed", + acquisitionError: "invalid_json", + }); + expect(invalidState?.monthlyUsage).toBeUndefined(); + expect((await session.rpc.usage.getMetrics()).totalNanoAiu).toBe(before.totalNanoAiu); + expect(requests.length).toBeGreaterThan(0); + expect(requests.every((request) => request === "GET /models" || request === "GET /quota")).toBe( + true + ); +}); diff --git a/nodejs/test/e2e/multi_provider_registry.e2e.test.ts b/nodejs/test/e2e/multi_provider_registry.e2e.test.ts index cd0eb53169..49b4a3618c 100644 --- a/nodejs/test/e2e/multi_provider_registry.e2e.test.ts +++ b/nodejs/test/e2e/multi_provider_registry.e2e.test.ts @@ -137,7 +137,8 @@ describe("Multi-provider BYOK registry", async () => { async function assertRouting( selectionId: string, expectedWireModel: string, - expectedProviderHeader: string + expectedProviderHeader: string, + expectedOutputCap?: number ): Promise { // Two OpenAI-compatible providers, both pointed at the replay proxy so // their /chat/completions traffic is captured. They are distinguished on @@ -162,7 +163,12 @@ describe("Multi-provider BYOK registry", async () => { }, ]; const models: ProviderModelConfig[] = [ - { id: "sonnet", provider: "alpha", wireModel: "byok-gpt-4o" }, + { + id: "sonnet", + provider: "alpha", + wireModel: "byok-gpt-4o", + maxOutputTokens: 16_384, + }, { id: "haiku", provider: "alpha", wireModel: "byok-gpt-4o-mini" }, { id: "turbo", provider: "delta", wireModel: "byok-gpt-4-turbo" }, ]; @@ -175,7 +181,8 @@ describe("Multi-provider BYOK registry", async () => { }); try { - await session.sendAndWait({ prompt: "What is 5+5?" }); + const response = await session.sendAndWait({ prompt: "What is 5+5?" }); + expect(response?.data.content).toContain("10"); const exchanges = await waitForExchanges(); expect(exchanges.length).toBe(1); const exchange = exchanges[0]; @@ -183,6 +190,9 @@ describe("Multi-provider BYOK registry", async () => { // The wire model sent to the provider is the selected model's // wireModel, not its provider-qualified selection id. expect(exchange.request.model).toBe(expectedWireModel); + expect(exchange.request.max_tokens).toBe(expectedOutputCap); + expect(exchange.request.max_completion_tokens).toBeUndefined(); + expect("max_output_tokens" in exchange.request).toBe(false); // The request carried the owning provider's custom header, proving // the turn was dispatched against the correct provider connection. @@ -200,7 +210,7 @@ describe("Multi-provider BYOK registry", async () => { } it("should route alpha sonnet turn to its provider and wire model", async () => { - await assertRouting("alpha/sonnet", "byok-gpt-4o", "alpha"); + await assertRouting("alpha/sonnet", "byok-gpt-4o", "alpha", 16_384); }); it("should route alpha haiku turn to its provider and wire model", async () => { diff --git a/nodejs/test/e2e/pending_work_resume.e2e.test.ts b/nodejs/test/e2e/pending_work_resume.e2e.test.ts index b4bf6fa352..ecb5294a59 100644 --- a/nodejs/test/e2e/pending_work_resume.e2e.test.ts +++ b/nodejs/test/e2e/pending_work_resume.e2e.test.ts @@ -602,18 +602,30 @@ describe("Pending work resume", async () => { .filter(Boolean) .map((line) => JSON.parse(line) as SessionEvent); }; + // Live SDK callbacks do not prove that both parallel boundaries reached the journal. await waitForCondition( - () => - readPersistedEvents().some( - (event) => - event.type === "tool.execution_complete" && - event.data.toolCallId === toolA.data.toolCallId && - event.data.success && - event.data.result?.content === "PARALLEL_A_ALPHA" - ), + () => { + const persistedEvents = readPersistedEvents(); + return ( + persistedEvents.some( + (event) => + event.type === "tool.execution_complete" && + event.data.toolCallId === toolA.data.toolCallId && + event.data.success && + event.data.result?.content === "PARALLEL_A_ALPHA" + ) && + persistedEvents.some( + (event) => + event.type === "external_tool.requested" && + event.data.requestId === toolB.data.requestId && + event.data.toolCallId === toolB.data.toolCallId + ) + ); + }, { timeoutMs: PENDING_WORK_TIMEOUT_MS, - timeoutMessage: "Tool A's result was not flushed to events.jsonl before B", + timeoutMessage: + "Tool A's result and tool B's pending request were not both flushed to events.jsonl before shutdown", } ); expect( diff --git a/nodejs/test/e2e/rpc_mcp_lifecycle.e2e.test.ts b/nodejs/test/e2e/rpc_mcp_lifecycle.e2e.test.ts index 40f837434d..b47eed190d 100644 --- a/nodejs/test/e2e/rpc_mcp_lifecycle.e2e.test.ts +++ b/nodejs/test/e2e/rpc_mcp_lifecycle.e2e.test.ts @@ -86,6 +86,103 @@ describe("Session-scoped MCP lifecycle RPC", async () => { expect(message.toLowerCase()).not.toContain("unhandled method"); } + async function offeredToolNames(session: CopilotSession): Promise { + await session.rpc.tools.initializeAndValidate(); + const { tools } = await session.rpc.tools.getCurrentMetadata(); + if (tools === null) { + throw new Error("initializeAndValidate did not initialize session tools"); + } + return tools.map((tool) => tool.name); + } + + it("should start an ephemeral server from caller config and reuse its replacement", async () => { + const serverName = missingName("ephemeral"); + const session = await client.createSession({ onPermissionRequest: approveAll }); + try { + const persistedBefore = await client.rpc.mcp.config.list(); + const initialConfig = { + type: "stdio" as const, + command: "node", + args: [TEST_MCP_SERVER, "--tool-name", "ephemeral_initial"], + cwd: TEST_HARNESS_DIR, + excludeTools: ["ephemeral_initial"], + disableToolCache: true, + }; + await session.rpc.mcp.startServer({ serverName, config: initialConfig }); + expect( + (await session.rpc.mcp.listTools({ serverName })).tools.map((tool) => tool.name) + ).toEqual(["ephemeral_initial"]); + expect(await offeredToolNames(session)).not.toContain( + `${serverName}-ephemeral_initial` + ); + expect((await session.rpc.mcp.isServerRunning({ serverName })).running).toBe(true); + + await session.rpc.mcp.restartServer({ + serverName, + config: { + ...initialConfig, + args: [TEST_MCP_SERVER, "--tool-name", "ephemeral_replacement"], + }, + }); + expect(await offeredToolNames(session)).toContain( + `${serverName}-ephemeral_replacement` + ); + await session.rpc.mcp.stopServer({ serverName }); + expect((await session.rpc.mcp.isServerRunning({ serverName })).running).toBe(false); + await session.rpc.mcp.startServer({ serverName }); + await session.rpc.mcp.restartServer({ serverName }); + expect( + (await session.rpc.mcp.listTools({ serverName })).tools.map((tool) => tool.name) + ).toEqual(["ephemeral_replacement"]); + expect(await client.rpc.mcp.config.list()).toEqual(persistedBefore); + } finally { + await session.disconnect(); + } + const nextSession = await client.createSession({ onPermissionRequest: approveAll }); + try { + expect( + (await nextSession.rpc.mcp.listConfigured()).servers.some( + (server) => server.name === serverName + ) + ).toBe(false); + } finally { + await nextSession.disconnect(); + } + }); + + it("should replace the registered config when restarting an installed server", async () => { + const serverName = "rpc-lifecycle-replace-server"; + const session = await createSessionWithMcp(serverName); + try { + await waitForMcpServerStatus(session, serverName); + expect( + (await session.rpc.mcp.listTools({ serverName })).tools.map((tool) => tool.name) + ).toEqual(["get_env"]); + expect(await offeredToolNames(session)).toContain(`${serverName}-get_env`); + await session.rpc.mcp.restartServer({ + serverName, + config: { + type: "stdio", + command: "node", + args: [TEST_MCP_SERVER, "--tool-name", "installed_replacement"], + cwd: TEST_HARNESS_DIR, + tools: ["*"], + excludeTools: ["installed_replacement"], + disableToolCache: true, + }, + }); + expect( + (await session.rpc.mcp.listTools({ serverName })).tools.map((tool) => tool.name) + ).toEqual(["installed_replacement"]); + const offered = await offeredToolNames(session); + expect(offered).not.toContain(`${serverName}-installed_replacement`); + expect(offered).not.toContain(`${serverName}-get_env`); + expect((await session.rpc.mcp.isServerRunning({ serverName })).running).toBe(true); + } finally { + await session.disconnect(); + } + }); + it( "should list tools and report running status for connected server", { timeout: 120_000 }, diff --git a/nodejs/test/e2e/rpc_server.e2e.test.ts b/nodejs/test/e2e/rpc_server.e2e.test.ts index 07d7ffcf51..16e3d1411a 100644 --- a/nodejs/test/e2e/rpc_server.e2e.test.ts +++ b/nodejs/test/e2e/rpc_server.e2e.test.ts @@ -4,6 +4,7 @@ import * as fs from "fs"; import * as path from "path"; +import { execFileSync } from "node:child_process"; import { randomUUID } from "node:crypto"; import { describe, expect, it, onTestFinished } from "vitest"; import { CopilotClient, RuntimeConnection } from "../../src/index.js"; @@ -113,6 +114,12 @@ describe("Server-scoped RPC", async () => { const policy = { model: "auto", autoTier: "balance", + permissions: { + limitTo: ["Domain(*.example.com)"], + deny: ["Domain(blocked.example.com)"], + ask: ["Domain(prompt.example.com)"], + allow: ["Domain(allowed.example.com)"], + }, }; const concretePolicy = { model: "gpt-5", @@ -175,6 +182,43 @@ describe("Server-scoped RPC", async () => { const absent = await api.compose({ layers: [{ source: "device" }] }); expect(absent.resolved.source).toBe("none"); + const permissionsContext = resolved.permissionsContext; + if (!permissionsContext) { + throw new Error("Runtime omitted the managed permission evaluation context"); + } + expect( + await api.permissions.evaluate({ + context: permissionsContext, + operations: [ + { kind: "url", url: "https://allowed.example.com/guide" }, + { kind: "url", url: "https://prompt.example.com/" }, + { kind: "url", url: "https://blocked.example.com/" }, + { kind: "url", url: "https://outside.test/" }, + ], + }) + ).toEqual({ + failClosed: false, + results: [ + { + operation: { kind: "url", url: "https://allowed.example.com/guide" }, + verdict: "allow", + }, + { operation: { kind: "url", url: "https://prompt.example.com/" }, verdict: "ask" }, + { + operation: { kind: "url", url: "https://blocked.example.com/" }, + verdict: "deny", + }, + { operation: { kind: "url", url: "https://outside.test/" }, verdict: "deny" }, + ], + }); + await expect( + api.permissions.evaluate({ + context: permissionsContext, + operations: [{ kind: "url", url: "not-a-url" }], + }) + ).rejects.toMatchObject({ code: -32602 }); + await expect(api.resolve({ selectionId: "missing-account" })).rejects.toThrow(); + const managedSession = await policyClient.createSession({ enableManagedSettings: true, model: "auto", @@ -647,6 +691,37 @@ describe("Server-scoped RPC", async () => { ); }); + it("should discover all 128 custom agents including deeply nested definitions", async () => { + const projectPath = createUniqueWorkDirectory("server-rpc-agents"); + execFileSync("git", ["init", "--quiet"], { + cwd: projectPath, + env, + windowsHide: true, + }); + const agentsDirectory = path.join(projectPath, ".github", "agents"); + const nestedDirectory = path.join(agentsDirectory, ...Array(12).fill("d")); + fs.mkdirSync(nestedDirectory, { recursive: true }); + const expectedNames = Array.from( + { length: 128 }, + (_, index) => `agent-${index.toString().padStart(3, "0")}` + ); + for (const [index, name] of expectedNames.entries()) { + const directory = index === 127 ? nestedDirectory : agentsDirectory; + fs.writeFileSync( + path.join(directory, `${name}.agent.md`), + `---\nname: ${name}\ndescription: Test agent.\n---\nAgent prompt.` + ); + } + + await client.start(); + const result = await client.rpc.agents.discover({ + projectPaths: [projectPath], + excludeHostAgents: true, + }); + + expect(result.agents.map((agent) => agent.name).sort()).toEqual(expectedNames); + }); + it("should discover server mcp and skills", async () => { await client.start(); diff --git a/nodejs/test/e2e/rpc_server_plugins.e2e.test.ts b/nodejs/test/e2e/rpc_server_plugins.e2e.test.ts index 20575a9114..2ce0800f4a 100644 --- a/nodejs/test/e2e/rpc_server_plugins.e2e.test.ts +++ b/nodejs/test/e2e/rpc_server_plugins.e2e.test.ts @@ -3,15 +3,25 @@ *--------------------------------------------------------------------------------------------*/ import { randomUUID } from "node:crypto"; -import { mkdirSync, rmSync, writeFileSync } from "node:fs"; -import { join } from "node:path"; -import { describe, expect, it } from "vitest"; -import { CopilotClient, RuntimeConnection } from "../../src/index.js"; +import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { join, dirname } from "node:path"; +import { fileURLToPath } from "node:url"; +import { describe, expect, it, onTestFinished } from "vitest"; +import { + approveAll, + CopilotClient, + RuntimeConnection, + type CopilotSession, +} from "../../src/index.js"; import { createSdkTestContext, DEFAULT_GITHUB_TOKEN } from "./harness/sdkTestContext.js"; +import { waitForCondition } from "./harness/sdkTestHelper.js"; const MARKETPLACE_NAME = "csharp-e2e-marketplace"; const PLUGIN_NAME = "csharp-e2e-plugin"; const DIRECT_PLUGIN_NAME = "csharp-e2e-direct"; +const TEST_MCP_SERVER = fileURLToPath( + new URL("../../../test/harness/test-mcp-server.mjs", import.meta.url) +); describe("Server-scoped plugin RPC", async () => { const { env, workDir } = await createSdkTestContext(); @@ -127,6 +137,110 @@ This skill exists so the plugin reports at least one installed skill. writeFileSync(join(pluginDir, "SKILL.md"), skill); } + it( + "keeps unrelated MCP servers running when Computer Use is toggled", + { timeout: 120_000 }, + async ({ expect }) => { + const pluginDir = createUniqueDirectory("computer-use"); + const { client, home } = await createIsolatedStartedClient(); + let session: CopilotSession | undefined; + onTestFinished(async () => { + try { + await session?.disconnect(); + } finally { + await disposeIsolated(client, home, pluginDir); + } + }); + writeFileSync( + join(pluginDir, "plugin.json"), + JSON.stringify({ + name: "computer-use", + version: "1.0.0", + mcpServers: "./.mcp.json", + }) + ); + writeFileSync( + join(pluginDir, ".mcp.json"), + JSON.stringify({ + mcpServers: { + "computer-use": { + command: process.execPath, + args: [TEST_MCP_SERVER, "--server-name", "computer-use"], + cwd: dirname(TEST_MCP_SERVER), + tools: ["*"], + }, + }, + }) + ); + await client.rpc.plugins.builtin.set({ paths: [pluginDir] }); + const serverName = "unrelated-to-computer-use"; + const startupMarker = join(home, "unrelated-mcp-starts.log"); + const mcpSession = await client.createSession({ + onPermissionRequest: approveAll, + mcpServers: { + [serverName]: { + type: "local", + command: process.execPath, + args: [ + TEST_MCP_SERVER, + "--startup-marker", + startupMarker, + "--server-name", + serverName, + ], + workingDirectory: dirname(TEST_MCP_SERVER), + tools: ["*"], + }, + }, + }); + session = mcpSession; + const waitForConnected = (name: string) => + waitForCondition( + async () => + (await mcpSession.rpc.mcp.list()).servers.some( + (server) => server.name === name && server.status === "connected" + ), + { + timeoutMs: 60_000, + intervalMs: 200, + timeoutMessage: `${name} did not connect`, + } + ); + await waitForConnected(serverName); + expect(readFileSync(startupMarker, "utf8")).toBe(`${serverName}\n`); + const toolsBefore = await mcpSession.rpc.mcp.listTools({ serverName }); + expect(toolsBefore.tools.map((tool) => tool.name)).toContain("get_env"); + expect( + (await mcpSession.rpc.mcp.list()).servers.map((server) => server.name) + ).not.toContain("computer-use"); + + for (const enabled of [true, false, true, false]) { + if (enabled) { + await client.rpc.plugins.enable({ names: ["computer-use"] }); + await waitForConnected("computer-use"); + } else { + await client.rpc.plugins.disable({ names: ["computer-use"] }); + await waitForCondition( + async () => + !(await mcpSession.rpc.mcp.list()).servers.some( + (server) => server.name === "computer-use" + ), + { + timeoutMs: 60_000, + intervalMs: 200, + timeoutMessage: "Computer Use MCP server was not removed", + } + ); + } + expect((await mcpSession.rpc.mcp.isServerRunning({ serverName })).running).toBe( + true + ); + expect(await mcpSession.rpc.mcp.listTools({ serverName })).toEqual(toolsBefore); + expect(readFileSync(startupMarker, "utf8")).toBe(`${serverName}\n`); + } + } + ); + it("should install and list plugin from local marketplace", { timeout: 120_000 }, async () => { const marketplaceDir = createLocalMarketplaceFixture(); const { client, home } = await createIsolatedStartedClient(); diff --git a/nodejs/test/e2e/rpc_session_state.e2e.test.ts b/nodejs/test/e2e/rpc_session_state.e2e.test.ts index 76e7ff2261..9efa6dfdc9 100644 --- a/nodejs/test/e2e/rpc_session_state.e2e.test.ts +++ b/nodejs/test/e2e/rpc_session_state.e2e.test.ts @@ -304,6 +304,60 @@ describe("Session-scoped RPC", async () => { } }); + it("should read the reasoning-summaries policy for the current model", async () => { + const session = await client.createSession({ + onPermissionRequest: approveAll, + model: "claude-sonnet-5", + // This policy needs the CAPI catalog; legacy BYOK sessions skip it. + // An explicit provider roster keeps the harness from replacing CAPI. + providers: [], + }); + + try { + // `ready` stays false until the session has resolved its model + // catalog, which only happens once something asks for it. Prime the + // session's model-list cache so the policy reports the real answer + // instead of the not-ready placeholder. + await session.rpc.model.list(); + + const policy = await session.rpc.model.getReasoningSummaries({}); + expect(policy.ready).toBe(true); + expect(typeof policy.experimentSettled).toBe("boolean"); + expect(typeof policy.modelDefaultShowsSummaries).toBe("boolean"); + expect(typeof policy.showReasoningHeaders).toBe("boolean"); + // No session-level reasoning-summary override, so the session + // follows the selected model's default. + expect(policy.sessionShowsSummaries).toBe(policy.modelDefaultShowsSummaries); + + // A persisted setting decides visibility whatever the experiment + // assignment is, and one that restates the model default is + // reported as redundant. + const redundant = await session.rpc.model.getReasoningSummaries({ + persistedShowReasoning: policy.modelDefaultShowsSummaries, + }); + expect(redundant.visibility).toBe(policy.modelDefaultShowsSummaries); + expect(redundant.clearRedundantSetting).toBe(true); + + const opposed = await session.rpc.model.getReasoningSummaries({ + persistedShowReasoning: !policy.modelDefaultShowsSummaries, + }); + expect(opposed.visibility).toBe(!policy.modelDefaultShowsSummaries); + expect(opposed.clearRedundantSetting).toBe(false); + + // A visible view never asks for the "summaries are hidden" hint, + // and a pending experiment assignment never asks for it either. + const visible = await session.rpc.model.getReasoningSummaries({ + summariesVisible: true, + }); + expect(visible.showHiddenHint).toBe(false); + if (!policy.experimentSettled) { + expect(policy.showHiddenHint).toBe(false); + } + } finally { + await session.disconnect(); + } + }); + it("should get and set session metadata", async () => { const session = await client.createSession({ onPermissionRequest: approveAll }); diff --git a/nodejs/test/e2e/rpc_settings.e2e.test.ts b/nodejs/test/e2e/rpc_settings.e2e.test.ts new file mode 100644 index 0000000000..7d0ce82023 --- /dev/null +++ b/nodejs/test/e2e/rpc_settings.e2e.test.ts @@ -0,0 +1,138 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { spawn } from "node:child_process"; +import { once } from "node:events"; +import { mkdir, readFile, writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import { describe, it, onTestFinished } from "vitest"; +import { + createMessageConnection, + StreamMessageReader, + StreamMessageWriter, +} from "vscode-jsonrpc/node.js"; +import { createInternalServerRpc, createServerRpc } from "../../src/generated/rpc.js"; +import { createSdkTestContext, getLegacyCliPathForTests } from "./harness/sdkTestContext.js"; +import { stopChildProcess } from "./harness/sdkTestHelper.js"; + +describe("Internal settings RPC through the CLI SDK server", async () => { + const { env, workDir } = await createSdkTestContext(); + + it("preserves settings semantics through the generated SDK methods", async ({ expect }) => { + const home = join(workDir, "settings-home"); + const repoRoot = join(workDir, "repository"); + await mkdir(home, { recursive: true }); + await mkdir(join(repoRoot, ".github", "copilot"), { recursive: true }); + const legacy = { model: "legacy-model", render_markdown: false }; + await writeFile(join(home, "config.json"), JSON.stringify(legacy)); + + // These methods are internal, so use their generated projection over a real CLI transport. + const cliPath = await getLegacyCliPathForTests(); + const args = ["--headless", "--stdio", "--no-auto-update", "--log-level", "error"]; + const child = spawn( + cliPath.endsWith(".js") ? process.execPath : cliPath, + cliPath.endsWith(".js") ? [cliPath, ...args] : args, + { cwd: workDir, env: { ...env, COPILOT_HOME: home }, stdio: "pipe" } + ); + let stderr = ""; + child.stderr.on("data", (chunk) => { + stderr += String(chunk); + }); + const connection = createMessageConnection( + new StreamMessageReader(child.stdout), + new StreamMessageWriter(child.stdin) + ); + onTestFinished(async () => { + connection.dispose(); + await stopChildProcess(child); + }); + await once(child, "spawn"); + connection.listen(); + const rpc = createInternalServerRpc(connection); + expect(await rpc.connect({}), stderr).toMatchObject({ + protocolVersion: expect.any(Number), + }); + + const paths = await rpc.user.settings.getPaths({ repoRoot }); + expect(paths).toEqual({ + userSettings: join(home, "settings.json"), + repoSettings: join(repoRoot, ".github", "copilot", "settings.json"), + repoLocalSettings: join(repoRoot, ".github", "copilot", "settings.local.json"), + }); + expect((await rpc.user.settings.listGovernanceKeys()).repo).toContain("model"); + expect(await rpc.user.settings.normalize({ settings: { render_markdown: true } })).toEqual({ + settings: { renderMarkdown: true }, + }); + const problems = await rpc.user.settings.diagnoseProblems({ + sources: [ + { + kind: "user", + label: "User", + filePath: paths.userSettings, + config: { notASetting: true }, + }, + ], + }); + expect(problems.problems).toEqual([ + expect.objectContaining({ kind: "unknown-key", rawKey: "notASetting" }), + ]); + + await rpc.user.settings.writeAll({ settings: { model: "initial-model", beep: true } }); + expect( + await rpc.user.settings.edit({ + canonicalPath: ["model"], + operation: "set", + value: "edited-model", + }) + ).toEqual({ status: "changed" }); + expect( + await rpc.user.settings.applyTopKey({ + canonicalPath: ["model"], + topKey: "model", + value: null, + }) + ).toMatchObject({ status: "validation-error" }); + expect( + await rpc.user.settings.applyTopKey({ + canonicalPath: ["model"], + topKey: "model", + value: "applied-model", + }) + ).toEqual({ status: "ok" }); + expect(JSON.parse(await readFile(paths.userSettings, "utf8"))).toEqual({ + model: "applied-model", + beep: true, + }); + await rpc.user.settings.writeAll({ settings: { model: "replacement-model" } }); + expect(JSON.parse(await readFile(paths.userSettings, "utf8"))).toEqual({ + model: "replacement-model", + }); + const loaded = await createServerRpc(connection).user.settings.get(); + expect(loaded.settings.model.value).toBe("replacement-model"); + + const repoPath = join(repoRoot, ".github", "copilot", "settings.json"); + await writeFile(repoPath, '{"custom":{"items":[true,null,12.5]}}'); + const target = { repoRoot, scope: "repo" }; + await rpc.repo.settings.writeKey({ ...target, key: "model", value: "repo-model" }); + expect(await rpc.repo.settings.readRaw(target)).toEqual({ + config: { custom: { items: [true, null, 12.5] }, model: "repo-model" }, + invalid: false, + }); + await rpc.repo.settings.writeAll({ ...target, settings: {} }); + expect(await rpc.repo.settings.readRaw(target)).toEqual({ + config: { custom: { items: [true, null, 12.5] } }, + invalid: false, + }); + await writeFile(repoPath, "{ invalid"); + expect(await rpc.repo.settings.readRaw(target)).toEqual({ config: {}, invalid: true }); + await expect( + rpc.repo.settings.writeKey({ + ...target, + key: "model", + value: "must-not-replace-invalid-file", + }) + ).rejects.toThrow(); + expect(await readFile(repoPath, "utf8")).toBe("{ invalid"); + }); +}); diff --git a/nodejs/test/e2e/rpc_shell_and_fleet.e2e.test.ts b/nodejs/test/e2e/rpc_shell_and_fleet.e2e.test.ts index 4f8061c960..51e5855e13 100644 --- a/nodejs/test/e2e/rpc_shell_and_fleet.e2e.test.ts +++ b/nodejs/test/e2e/rpc_shell_and_fleet.e2e.test.ts @@ -14,7 +14,12 @@ import { createSdkTestContext } from "./harness/sdkTestContext.js"; import { waitForCondition, waitForFileText } from "./harness/sdkTestHelper.js"; describe("Shell and fleet RPC", async () => { - const { copilotClient: client, workDir } = await createSdkTestContext(); + const { copilotClient: client, workDir } = await createSdkTestContext({ + logLevel: "debug", + copilotClientOptions: { + env: { RUST_LOG: "error,sdk_diagnostics=debug" }, + }, + }); function createWriteFileCommand(markerPath: string, marker: string): string { if (os.platform() === "win32") { @@ -23,6 +28,12 @@ describe("Shell and fleet RPC", async () => { return `sh -c "printf '%s' '${marker}' > '${markerPath}'"`; } + function createWindowsBatchCommand(command: string): string { + const filename = `shell-command-${randomUUID()}.cmd`; + fs.writeFileSync(path.join(workDir, filename), `@echo off\r\n${command}\r\n`); + return `cmd.exe /d /c ${filename}`; + } + async function waitForMessages( session: CopilotSession, predicate: (events: SessionEvent[]) => boolean, @@ -140,7 +151,9 @@ describe("Shell and fleet RPC", async () => { const timeout = os.platform() === "win32" ? 10_000 : 1_000; const command = os.platform() === "win32" - ? `echo started>"${started}" & ping.exe -n 31 127.0.0.1 >nul & echo completed>"${completed}"` + ? createWindowsBatchCommand( + `echo started>"${started}" & ping.exe -n 31 127.0.0.1 >nul & echo completed>"${completed}"` + ) : `printf started > '${started}'; sleep 30; printf completed > '${completed}'`; try { const result = await session.rpc.shell.exec({ command, cwd: workDir, timeout }); @@ -160,13 +173,120 @@ describe("Shell and fleet RPC", async () => { } ); + it.skipIf(os.platform() === "win32")( + "should release post-root-exit shell pipes and rewind admission at timeout", + async ({ onTestFailed }) => { + const cwd = fs.mkdtempSync(path.join(workDir, "post-exit-shell-")); + let phase = "creating session"; + let rootPid: number | undefined; + let rootExited = false; + let lastRewindState: + | Awaited> + | undefined; + onTestFailed(() => { + console.error("Post-root-exit shell timeout diagnostic:", { + phase, + rootPid, + rootExited, + lastRewindState, + descendantReady: fs.existsSync(path.join(cwd, "descendant.ready")), + terminated: fs.existsSync(path.join(cwd, "terminated")), + }); + }); + const session = await client.createSession({ + enableFileChangeTracking: true, + onPermissionRequest: approveAll, + }); + let processId: string | undefined; + function processExists(pid: number): boolean { + try { + process.kill(pid, 0); + return true; + } catch (error) { + if ((error as NodeJS.ErrnoException).code === "ESRCH") { + return false; + } + throw error; + } + } + try { + phase = "waiting for rewind tracking initialization"; + await waitForCondition( + async () => { + lastRewindState = await session.rpc.history.listRewindPoints(); + return lastRewindState.fileChangeTrackingEnabled; + }, + { timeoutMessage: "Rewind tracking did not initialize" } + ); + phase = "launching post-exit shell"; + // Reader teardown must not SIGPIPE the fixture before its TERM trap records termination. + const result = await session.rpc.shell.exec({ + command: + `sh -c 'trap "" PIPE; trap "printf terminated > terminated; exit 0" TERM; ` + + `printf ready > descendant.ready; sleep 60' & ` + + `echo $$ > root.pending; mv root.pending root.pid; exit 7`, + cwd, + timeout: 10_000, + }); + processId = result.processId; + expect(processId).toBeTruthy(); + phase = "waiting for root reap and descendant readiness"; + await waitForCondition( + () => { + if ( + !fs.existsSync(path.join(cwd, "root.pid")) || + !fs.existsSync(path.join(cwd, "descendant.ready")) + ) { + return false; + } + rootPid = Number(fs.readFileSync(path.join(cwd, "root.pid"), "utf8")); + expect(Number.isInteger(rootPid) && rootPid > 1).toBe(true); + rootExited = !processExists(rootPid); + return rootExited; + }, + { timeoutMessage: "Fixture root did not exit with a ready pipe holder" } + ); + phase = "checking retained rewind lease"; + expect(fs.existsSync(path.join(cwd, "terminated"))).toBe(false); + lastRewindState = await session.rpc.history.listRewindPoints(); + expect(lastRewindState.unavailableReason).toBe("session-busy"); + + // Neither poll sends a kill: the configured timeout must end the + // descendant and release the production sink's operation lease. + phase = "waiting for rewind admission after timeout"; + await waitForCondition( + async () => { + lastRewindState = await session.rpc.history.listRewindPoints(); + return ( + lastRewindState.fileChangeTrackingEnabled && + lastRewindState.unavailableReason === undefined + ); + }, + { timeoutMessage: "Post-exit timeout left rewind admission session-busy" } + ); + phase = "waiting for automatic descendant termination"; + await waitForFileText(path.join(cwd, "terminated"), "terminated"); + phase = "checking registry cleanup"; + expect((await session.rpc.shell.kill({ processId })).killed).toBe(false); + } finally { + try { + if (processId) { + await session.rpc.shell.kill({ processId, signal: "SIGKILL" }); + } + } finally { + await session.disconnect(); + } + } + } + ); + it("should accept a missing shell command and clean up after it exits", async () => { const session = await client.createSession({ onPermissionRequest: approveAll }); const marker = path.join(workDir, `shell-missing-${randomUUID()}.txt`); const missingCommand = `not-a-command-${randomUUID()}`; const command = os.platform() === "win32" - ? `${missingCommand} & echo done>"${marker}" & exit /b 1` + ? createWindowsBatchCommand(`${missingCommand} & echo done>"${marker}" & exit /b 1`) : `${missingCommand}; code=$?; printf done > '${marker}'; exit $code`; try { const result = await session.rpc.shell.exec({ command, cwd: workDir }); @@ -187,32 +307,40 @@ describe("Shell and fleet RPC", async () => { async (outputKind) => { const session = await client.createSession({ onPermissionRequest: approveAll }); const marker = path.join(workDir, `shell-${outputKind}-${randomUUID()}.txt`); + const outputFile = path.join(workDir, `shell-output-${randomUUID()}.txt`); + const started = `${marker}.started`; const phase = `${marker}.phase`; - const recordPhase = (value: string) => - `[IO.File]::WriteAllText('${phase}', '${value}')`; - // Publish only after Set-Content has closed its exclusive Windows writer. + const recordPhase = (value: string) => `echo ${value}>"${phase}"`; + // Run cmd syntax in an owned batch even when the RPC selects PowerShell. const publishMarker = - `${recordPhase("output-written")}; ` + - `Set-Content -LiteralPath '${marker}.pending' -Value done; ` + - `${recordPhase("marker-written")}; ` + - `Move-Item -LiteralPath '${marker}.pending' -Destination '${marker}'; ` + + `${recordPhase("output-written")} & ` + + `echo done>"${marker}.pending" & ` + + `${recordPhase("marker-written")} & ` + + `move /y "${marker}.pending" "${marker}" >nul & ` + recordPhase("marker-published"); const command = outputKind === "stderr" ? os.platform() === "win32" - ? `powershell -NoLogo -NoProfile -Command "${recordPhase("started")}; [Console]::Error.WriteLine('boom'); ${publishMarker}; exit 2"` - : `echo boom 1>&2; printf done > '${marker}'; exit 2` + ? createWindowsBatchCommand( + `${recordPhase("started")} & echo boom 1>&2 & ${publishMarker} & exit /b 2` + ) + : `printf started > '${started}'; echo boom 1>&2; printf done > '${marker}'; exit 2` : os.platform() === "win32" - ? `powershell -NoLogo -NoProfile -Command "${recordPhase("started")}; Write-Host ('x' * 71680); ${publishMarker}"` - : `printf '%71680s' '' | tr ' ' '='; printf done > '${marker}'`; + ? createWindowsBatchCommand( + `${recordPhase("started")} & type "${outputFile}" & ${publishMarker}` + ) + : `printf started > '${started}'; printf '%71680s' '' | tr ' ' '='; printf done > '${marker}'`; try { + if (outputKind === "large-stdout" && os.platform() === "win32") { + fs.writeFileSync(outputFile, "x".repeat(71680)); + } const result = await session.rpc.shell.exec({ command, cwd: workDir }); expect(result.processId).toBeTruthy(); await waitForFileText( marker, "done", 30_000, - os.platform() === "win32" ? [phase, `${marker}.pending`] : [] + os.platform() === "win32" ? [phase, `${marker}.pending`] : [started] ); // The process map is updated after the output stream closes. await new Promise((resolve) => setTimeout(resolve, 2_000)); diff --git a/nodejs/test/e2e/rpc_surface_coverage.e2e.test.ts b/nodejs/test/e2e/rpc_surface_coverage.e2e.test.ts index 9888df8a7b..30f4793d31 100644 --- a/nodejs/test/e2e/rpc_surface_coverage.e2e.test.ts +++ b/nodejs/test/e2e/rpc_surface_coverage.e2e.test.ts @@ -327,10 +327,33 @@ describe("Generated RPC surface coverage", () => { ...collectRuntimeFunctions(session.rpc, "session"), ]); - expect(inventory).toHaveLength(404); + expect(inventory).toHaveLength(410); + expect( + inventory.filter( + (method) => method.wireMethod === "session.model.getReasoningSummaries" + ) + ).toEqual([ + expect.objectContaining({ + scope: "session", + path: "model.getReasoningSummaries", + parameterCount: 1, + returnType: "Promise", + }), + ]); expect(inventory.filter((method) => method.wireMethod === "host.listSessions")).toEqual([ expect.objectContaining({ scope: "server", path: "host.listSessions" }), ]); + expect( + inventory.filter( + (method) => method.wireMethod === "managedSettings.permissions.evaluate" + ) + ).toEqual([ + expect.objectContaining({ + scope: "server", + path: "managedSettings.permissions.evaluate", + parameterCount: 1, + }), + ]); expect(inventory.some((method) => method.wireMethod === "user.settings.reload")).toBe( false ); @@ -341,6 +364,15 @@ describe("Generated RPC surface coverage", () => { path: "extensions.reconcile", parameterCount: 0, }); + expect( + inventory.filter((method) => method.wireMethod === "session.plugins.retryManaged") + ).toEqual([ + expect.objectContaining({ + scope: "session", + path: "plugins.retryManaged", + parameterCount: 1, + }), + ]); const sessionProviderMethods = [ "providers.getCatalog", "providers.discover", @@ -467,6 +499,7 @@ describe("Generated RPC surface coverage", () => { expect((await session.rpc.customizations.reload()).outcomes).toEqual([ { subsystem: "instructions", status: "reloaded" }, ]); + await session.rpc.plugins.retryManaged(); await expect( client.rpc.ping({ message: "error", __forceError: true } as never) @@ -496,5 +529,20 @@ describe("Generated RPC surface coverage", () => { { method: wireMethod, params: { sessionId: "rpc-surface-session" } }, ]); } + expect( + captured.filter((request) => request.method === "session.plugins.retryManaged") + ).toEqual([ + { + method: "session.plugins.retryManaged", + params: { + __coveragePath: "session.plugins.retryManaged", + sessionId: "rpc-surface-session", + }, + }, + { + method: "session.plugins.retryManaged", + params: { sessionId: "rpc-surface-session" }, + }, + ]); }); }); diff --git a/nodejs/test/e2e/rpc_tasks_and_handlers.e2e.test.ts b/nodejs/test/e2e/rpc_tasks_and_handlers.e2e.test.ts index 5f926f120c..e6e6a18c8f 100644 --- a/nodejs/test/e2e/rpc_tasks_and_handlers.e2e.test.ts +++ b/nodejs/test/e2e/rpc_tasks_and_handlers.e2e.test.ts @@ -7,7 +7,7 @@ import { z } from "zod"; import { approveAll, CopilotRequestHandler } from "../../src/index.js"; import type { SessionEvent, CopilotRequestContext, CopilotSession } from "../../src/index.js"; import { createSdkTestContext } from "./harness/sdkTestContext.js"; -import { waitForCondition } from "./harness/sdkTestHelper.js"; +import { getNextEventOfType, waitForCondition } from "./harness/sdkTestHelper.js"; import { isByokBackend } from "./harness/testBackend"; const capiIt = it.skipIf(isByokBackend); @@ -326,6 +326,96 @@ describe("Session tasks RPC and pending handlers", async () => { } }); + // Shell IDs count up per runtime process, so each attached-shell test owns its + // client: the recorded `shellId: 0` then holds regardless of test order. + async function withRunningAttachedShell( + run: ( + session: CopilotSession, + shellId: string, + eventTypes: SessionEvent["type"][], + replies: string[] + ) => Promise + ): Promise { + const shellClient = createClient(); + try { + const session = await shellClient.createSession({ onPermissionRequest: approveAll }); + const eventTypes: SessionEvent["type"][] = []; + const replies: string[] = []; + const unsubscribe = session.on((event) => { + eventTypes.push(event.type); + if (event.type === "assistant.message") { + replies.push(event.data.content ?? ""); + } + }); + try { + const assistantIdle = getNextEventOfType(session, "assistant.idle"); + await session.send({ + prompt: "Use your shell tool to run `sleep 600` in async mode without detaching. Do not read its output or wait for it. Then reply with exactly SHELL_STARTED.", + }); + await assistantIdle; + expect(replies.at(-1)).toContain("SHELL_STARTED"); + + const shell = (await session.rpc.tasks.list()).tasks.find( + (task) => task.type === "shell" && task.status === "running" + ); + expect(shell).toMatchObject({ attachmentMode: "attached" }); + await run(session, shell!.id, eventTypes, replies); + } finally { + unsubscribe(); + await session.disconnect(); + } + } finally { + await shellClient.stop(); + } + } + + it( + "should emit session idle after cancelling a running attached shell", + { timeout: 120_000 }, + async () => { + await withRunningAttachedShell(async (session, shellId, eventTypes) => { + // The running attached shell holds the turn's session.idle. + expect(eventTypes).not.toContain("session.idle"); + + expect((await session.rpc.tasks.cancel({ id: shellId })).cancelled).toBe(true); + await waitForCondition(() => eventTypes.includes("session.idle"), { + timeoutMessage: `session.idle never followed cancelling shell ${shellId}`, + }); + }); + } + ); + + it( + "should run a queued message after cancelling a running attached shell", + { timeout: 120_000 }, + async () => { + await withRunningAttachedShell(async (session, shellId, eventTypes, replies) => { + // The running attached shell holds idle, so an enqueued message waits behind it. + await session.send({ prompt: "Reply with exactly QUEUED_DONE.", mode: "enqueue" }); + await waitForCondition( + async () => + (await session.rpc.queue.pendingItems()).items.some((item) => + item.displayText.includes("QUEUED_DONE") + ), + { timeoutMessage: "The enqueued message was not parked behind the shell" } + ); + expect(replies.some((reply) => reply.includes("QUEUED_DONE"))).toBe(false); + expect(eventTypes).not.toContain("session.idle"); + + expect((await session.rpc.tasks.cancel({ id: shellId })).cancelled).toBe(true); + await waitForCondition( + () => replies.some((reply) => reply.includes("QUEUED_DONE")), + { + timeoutMessage: `The queued message never ran after cancelling shell ${shellId}`, + } + ); + await waitForCondition(() => eventTypes.includes("session.idle"), { + timeoutMessage: "session.idle never followed the queued message", + }); + }); + } + ); + it( "should apply a custom agent's composed model and effort when started over RPC", { timeout: 240_000 }, diff --git a/nodejs/test/e2e/sandbox_network.e2e.test.ts b/nodejs/test/e2e/sandbox_network.e2e.test.ts index 9e476d4aff..0a78088696 100644 --- a/nodejs/test/e2e/sandbox_network.e2e.test.ts +++ b/nodejs/test/e2e/sandbox_network.e2e.test.ts @@ -5,6 +5,7 @@ import { createServer } from "node:http"; import { describe, it } from "vitest"; import type { CopilotSession } from "../../src/index.js"; +import { getRuntimePlatform } from "../../src/runtimeArtifacts.js"; import { createSdkTestContext } from "./harness/sdkTestContext.js"; const TEST_NAME = @@ -17,6 +18,25 @@ describe("Sandbox network", async () => { }, }); + it("reports the shipped platform's sandbox support and refusal", async ({ expect }) => { + await client.start(); + const support = await client.rpc.sandbox.getHostSupport(); + if (getRuntimePlatform().startsWith("linuxmusl-")) { + expect(support).toEqual({ + supported: false, + reason: "Sandboxed shell execution is not supported on Alpine Linux/musl.", + capabilities: [], + }); + } else if (support.supported) { + expect(support.reason).toBeUndefined(); + expect(support.capabilities).toContainEqual({ name: "shell", supported: true }); + } else { + expect(support.reason).toEqual(expect.any(String)); + expect(support.reason).not.toBe(""); + expect(support.capabilities).toEqual([]); + } + }); + it(TEST_NAME, async ({ expect, onTestFinished, skip }) => { await client.start(); const support = await client.rpc.sandbox.getHostSupport(); diff --git a/nodejs/test/e2e/session_fs.e2e.test.ts b/nodejs/test/e2e/session_fs.e2e.test.ts index 61d15b9b26..963f80aae9 100644 --- a/nodejs/test/e2e/session_fs.e2e.test.ts +++ b/nodejs/test/e2e/session_fs.e2e.test.ts @@ -4,12 +4,13 @@ import { SessionCompactionCompleteEvent } from "@github/copilot/sdk"; import { MemoryProvider, VirtualProvider } from "@platformatic/vfs"; -import { createHash } from "crypto"; +import { createHash, randomUUID } from "crypto"; import { existsSync, mkdtempSync, realpathSync } from "fs"; -import { mkdir, readFile, rm, writeFile } from "fs/promises"; +import { mkdir, readFile, readdir, rm, writeFile } from "fs/promises"; import { createServer } from "http"; import { tmpdir } from "os"; import { join } from "path"; +import { fileURLToPath } from "url"; import { describe, expect, it, onTestFinished, vi } from "vitest"; import { CopilotClient } from "../../src/client.js"; import { getRuntimePlatform } from "../../src/runtimeArtifacts.js"; @@ -51,6 +52,7 @@ describe("Session Fs", async () => { createClient, env, openAiEndpoint, + workDir, } = await createSdkTestContext({ copilotClientOptions: { sessionFs: sessionFsConfig }, }); @@ -179,6 +181,131 @@ describe("Session Fs", async () => { expect(msg2?.data.content).toContain("300"); }); + it("should roll context through native tools.execute from an active SDK handler", async () => { + await openAiEndpoint.updateConfig({ + filePath: fileURLToPath( + new URL( + "../../../test/snapshots/session_fs/should_route_file_operations_through_the_session_fs_provider.yaml", + import.meta.url + ) + ), + workDir, + replayOnly: true, + }); + const original = await client.createSession({ + onPermissionRequest: approveAll, + createSessionFsProvider, + featureFlags: { CONTEXT_MANAGEMENT_TOOLS: false }, + }); + const sessionId = original.sessionId; + try { + expect( + (await original.sendAndWait({ prompt: "What is 100 + 200?" }))?.data.content + ).toContain("300"); + } finally { + await original.disconnect(); + } + + // Resume a valid large user event so a fresh window can reclaim context. + const request: SessionEvent = { + id: randomUUID(), + timestamp: new Date().toISOString(), + parentId: null, + type: "user.message", + data: { + content: + "Preserve SDK rollover state. " + "Rollover fixture context. ".repeat(3_000), + source: "user", + }, + }; + await provider.appendFile( + p(sessionId, `${sessionStatePath}/events.jsonl`), + JSON.stringify(request) + "\n" + ); + + let resumed: CopilotSession; + let innerResult: Awaited> | undefined; + resumed = await client.resumeSession(sessionId, { + onPermissionRequest: approveAll, + createSessionFsProvider, + featureFlags: { CONTEXT_MANAGEMENT_TOOLS: true }, + continuePendingWork: false, + tools: [ + defineTool("sdk_context_rollover", { + description: "Roll context from an active SDK tool handler", + isTerminal: true, + defer: "never", + handler: async () => { + innerResult = await resumed.rpc.tools.execute({ + name: "new_context", + arguments: {}, + toolCallId: "sdk-context-rollover-native", + }); + return JSON.stringify(innerResult); + }, + }), + ], + }); + try { + await resumed.rpc.tools.initializeAndValidate(); + expect((await resumed.rpc.tools.getCurrentMetadata()).tools).toEqual( + expect.arrayContaining([expect.objectContaining({ name: "new_context" })]) + ); + const remaining = await resumed.rpc.tools.execute({ + name: "get_context_remaining", + arguments: {}, + }); + expect(remaining, JSON.stringify(remaining)).toMatchObject({ resultType: "success" }); + expect(JSON.parse(remaining.textResultForLlm)).toMatchObject({ + context_window: { number: 1, previous_window_id: null }, + }); + const checkpoint = await resumed.rpc.tools.execute({ + name: "session_artifacts", + arguments: { + operation: "append", + path: "checkpoint.md", + content: "\nPreserve SDK rollover state.", + }, + }); + expect(checkpoint, JSON.stringify(checkpoint)).toMatchObject({ resultType: "success" }); + + const outerResult = await resumed.rpc.tools.execute({ + name: "sdk_context_rollover", + arguments: {}, + toolCallId: "sdk-context-rollover-handler", + }); + expect(outerResult, JSON.stringify(outerResult)).toMatchObject({ + resultType: "success", + }); + expect(innerResult, JSON.stringify(innerResult)).toMatchObject({ + resultType: "success", + }); + await expect + .poll(async () => + (await resumed.getEvents()).filter( + (event) => event.type === "session.context_cleared" + ) + ) + .toEqual([ + expect.objectContaining({ + data: expect.objectContaining({ + initialMessage: expect.stringContaining("Preserve SDK rollover state."), + }), + }), + ]); + const next = await resumed.rpc.tools.execute({ + name: "get_context_remaining", + arguments: {}, + }); + expect(next, JSON.stringify(next)).toMatchObject({ resultType: "success" }); + expect(JSON.parse(next.textResultForLlm)).toMatchObject({ + context_window: { number: 2, previous_window_id: expect.any(String) }, + }); + } finally { + await resumed.disconnect(); + } + }); + it("should reject setProvider when sessions already exist", async () => { const tcpConnectionToken = "session-fs-test-token"; const client = new CopilotClient({ @@ -307,6 +434,91 @@ describe("Session Fs", async () => { }); }); +describe("Session Fs native plan edits", async () => { + const { createClient, workDir } = await createSdkTestContext(); + + it.each(["interactive", "plan"] as const)( + "keeps native edits and write failures in the provider in %s mode", + async (mode) => { + const statePath = join(workDir, `provider-state-${mode}`); + const planPath = join(statePath, "plan.md"); + const provider = new MemoryProvider(); + const writes: string[] = []; + let rejectWrites = false; + const client = createClient({ + mode: "empty", + sessionFs: { + initialCwd: workDir, + sessionStatePath: statePath, + conventions: process.platform === "win32" ? "windows" : "posix", + }, + }); + onTestFinished(() => client.stop()); + const session = await client.createSession({ + onPermissionRequest: approveAll, + availableTools: ["edit"], + createSessionFsProvider: (session) => { + const handler = createTestSessionFsHandler(session, provider); + return { + ...handler, + async writeFile(path, content) { + if (path === planPath) { + writes.push(content); + if (rejectWrites) { + throw new Error("provider write denied"); + } + } + return handler.writeFile(path, content); + }, + }; + }, + }); + await session.rpc.mode.set({ mode }); + await session.rpc.plan.update({ content: "# Provider plan\n" }); + expect(await session.rpc.plan.read()).toMatchObject({ + path: planPath, + content: "# Provider plan\n", + }); + + // A writable host parent makes an accidental host write succeed. + await mkdir(statePath, { recursive: true }); + expect(await readdir(statePath)).toEqual([]); + const edit = (old_str: string, new_str: string) => + session.rpc.tools.execute({ + name: "edit", + arguments: { path: planPath, old_str, new_str }, + }); + await expect(edit("Provider plan", "Native update")).resolves.toMatchObject({ + resultType: "success", + }); + expect((await session.rpc.plan.read()).content).toBe("# Native update\n"); + expect(writes).toEqual(["# Provider plan\n", "# Native update\n"]); + expect(await readdir(statePath)).toEqual([]); + + await writeFile(planPath, "host-only content\n"); + await expect(edit("Native update", "Second update")).resolves.toMatchObject({ + resultType: "success", + }); + expect((await session.rpc.plan.read()).content).toBe("# Second update\n"); + expect(await readFile(planPath, "utf8")).toBe("host-only content\n"); + + rejectWrites = true; + const failed = await edit("Second update", "Rejected update"); + expect(failed.resultType).toBe("failure"); + expect(failed.textResultForLlm).toContain("provider write denied"); + expect((await session.rpc.plan.read()).content).toBe("# Second update\n"); + expect(writes).toEqual([ + "# Provider plan\n", + "# Native update\n", + "# Second update\n", + "# Rejected update\n", + ]); + expect(await readFile(planPath, "utf8")).toBe("host-only content\n"); + expect(await readdir(statePath)).toEqual(["plan.md"]); + } + ); +}); + describe("Session Fs remembered approvals", async () => { const workingDirectory = "/workspace"; const otherDirectory = "/provider-dir"; diff --git a/nodejs/test/e2e/skill_provider.e2e.test.ts b/nodejs/test/e2e/skill_provider.e2e.test.ts index 82d584809c..e8f2560625 100644 --- a/nodejs/test/e2e/skill_provider.e2e.test.ts +++ b/nodejs/test/e2e/skill_provider.e2e.test.ts @@ -6,10 +6,11 @@ import { execFileSync } from "child_process"; import * as fs from "fs"; import * as path from "path"; import { fileURLToPath } from "url"; -import { describe, expect, it } from "vitest"; +import { describe, expect, it, onTestFinished } from "vitest"; import { approveAll, RuntimeConnection, + type SessionConfig, type SessionEvent, type SkillProvider, type SkillProviderDescriptor, @@ -50,7 +51,12 @@ function skill(name: string, description: string, markdown: string): ProvidedSki } describe("Skill providers", async () => { - const { copilotClient: client, workDir } = await createSdkTestContext(); + const { + copilotClient: client, + createClient, + openAiEndpoint, + workDir, + } = await createSdkTestContext(); it("should load provider skill lazily through skill tool", async () => { // Body-only content: the catalog descriptor supplies all of the metadata. @@ -175,6 +181,83 @@ describe("Skill providers", async () => { } }); + it("should preserve skill prompt bytes across cold resume with reordered discovery", async () => { + const directories = ["custom-b", "custom-a"].map((name) => { + const directory = path.join(workDir, name); + fs.mkdirSync(directory, { recursive: true }); + fs.writeFileSync( + path.join(directory, "SKILL.md"), + `---\nname: ${name}\ndescription: An ordering fixture.\n---\nOrdering fixture.\n` + ); + return directory; + }); + const provided = ["provider-c", "provider-a", "provider-b"].map((name) => + skill(name, "An ordering fixture.", "Ordering fixture.\n") + ); + const config = { + model: "claude-sonnet-5", + onPermissionRequest: approveAll, + availableTools: ["skill"], + enableConfigDiscovery: false, + includedBuiltinSkills: [], + skipCustomInstructions: true, + skipEmbeddingRetrieval: true, + } satisfies SessionConfig; + const first = createClient({ connection: RuntimeConnection.forStdio() }); + onTestFinished(() => first.stop()); + const session = await first.createSession({ + ...config, + skillProvider: new TestSkillProvider(provided), + skillDirectories: directories, + }); + const response = await session.sendAndWait({ + prompt: "Without using tools, reply with exactly SKILL_ORDER_INITIAL_OK.", + }); + // Validate the final assistant response arrived (guards against truncated captures) + expect(response?.data.content).toContain("SKILL_ORDER_INITIAL_OK"); + const beforeRequests = await openAiEndpoint.getRequests(); + const beforeBlocks = beforeRequests.flatMap( + (request) => + request.body.match(/\\n[\s\S]*?<\/available_skills>/g) ?? [] + ); + expect(beforeBlocks.length).toBeGreaterThan(0); + for (const block of beforeBlocks) { + for (const name of [ + ...provided.map((entry) => entry.descriptor.name), + "custom-a", + "custom-b", + ]) { + expect(block).toContain(`${name}`); + } + expect(block.indexOf("provider-c")).toBeLessThan( + block.indexOf("custom-a") + ); + } + await session.disconnect(); + expect(await first.stop()).toHaveLength(0); + + const second = createClient({ connection: RuntimeConnection.forStdio() }); + onTestFinished(() => second.stop()); + const resumed = await second.resumeSession(session.sessionId, { + ...config, + skillProvider: new TestSkillProvider([provided[2], provided[0], provided[1]]), + skillDirectories: [...directories].reverse(), + }); + const resumedResponse = await resumed.sendAndWait({ + prompt: "Without using tools, reply with exactly SKILL_ORDER_RESUMED_OK.", + }); + // Validate the final assistant response arrived (guards against truncated captures) + expect(resumedResponse?.data.content).toContain("SKILL_ORDER_RESUMED_OK"); + const afterBlocks = (await openAiEndpoint.getRequests()) + .slice(beforeRequests.length) + .flatMap( + (request) => + request.body.match(/\\n[\s\S]*?<\/available_skills>/g) ?? [] + ); + expect(afterBlocks).toEqual(beforeBlocks); + await resumed.disconnect(); + }); + it("should report provider read failure without leaking details", async () => { const secret = "PROVIDER_SECRET_7F3A9C"; const provider = new TestSkillProvider([ diff --git a/nodejs/test/e2e/startup_policy.e2e.test.ts b/nodejs/test/e2e/startup_policy.e2e.test.ts new file mode 100644 index 0000000000..330cf3c44b --- /dev/null +++ b/nodejs/test/e2e/startup_policy.e2e.test.ts @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { describe, expect, it } from "vitest"; +import { createSdkTestContext } from "./harness/sdkTestContext.js"; + +describe("Sandbox proxy CA policy", async () => { + const { copilotClient: client } = await createSdkTestContext(); + + it("reports relevance without installation or caller config changes", async () => { + await client.start(); + const configured = { sandboxConfig: { enabled: true, auth: { gh: true } } }; + const before = structuredClone(configured); + expect(await client.rpc.sandbox.proxyCa.checkRelevance(configured)).toBe(true); + expect(configured).toEqual(before); + expect( + await client.rpc.sandbox.proxyCa.checkRelevance({ + sandboxConfig: { enabled: false, auth: { gh: true } }, + }) + ).toBe(false); + expect(await client.rpc.sandbox.proxyCa.checkRelevance({})).toBe(false); + }); + + it("reports installation support without an elevation prompt", async () => { + await client.start(); + const supported = await client.rpc.sandbox.proxyCa.checkInstallationSupport(); + expect(typeof supported).toBe("boolean"); + if (process.platform !== "win32") { + expect(supported).toBe(true); + } + }); +}); diff --git a/nodejs/test/e2e/structured_output.e2e.test.ts b/nodejs/test/e2e/structured_output.e2e.test.ts index ae08a9d16b..fbc84ab261 100644 --- a/nodejs/test/e2e/structured_output.e2e.test.ts +++ b/nodejs/test/e2e/structured_output.e2e.test.ts @@ -44,7 +44,7 @@ describe("Structured output", async () => { it("infers_typed_result_after_custom_tool", async () => { let calls = 0; const schema = z.object({ count: z.number().int(), color: z.string() }); - const session = await client.createSession({ + await using session = await client.createSession({ model, provider, onPermissionRequest: approveAll, @@ -89,8 +89,7 @@ describe("Structured output", async () => { it("typed_result_after_terminal_tool_and_steering", async () => { const events: SessionEvent[] = []; let calls = 0; - let session: CopilotSession; - session = await client.createSession({ + await using session: CopilotSession = await client.createSession({ model, provider, onPermissionRequest: approveAll, @@ -146,7 +145,7 @@ describe("Structured output", async () => { let calls = 0; let stops = 0; const replies: AssistantMessageEvent[] = []; - const session = await client.createSession({ + await using session = await client.createSession({ model, provider, onPermissionRequest: approveAll, @@ -204,7 +203,7 @@ describe("Structured output", async () => { it("rejects_unsupported_or_oversized_schemas_before_admission", async () => { for (const model of ["gpt-4.1", "hydrafusion"]) { - const session = await client.createSession({ + await using session = await client.createSession({ model, provider, ...(model === "hydrafusion" @@ -258,7 +257,7 @@ describe("Structured output", async () => { const hookReleased = new Promise((resolve) => { releaseHook = resolve; }); - const session = await client.createSession({ + await using session = await client.createSession({ model, provider, onPermissionRequest: approveAll, @@ -279,14 +278,20 @@ describe("Structured output", async () => { }, }); const replies: AssistantMessageEvent[] = []; - const errors: string[] = []; + const errors: "session.error"[] = []; + let errorCount = 0; + const startedAt = Date.now(); + const progress: { type: SessionEvent["type"]; elapsedMs: number }[] = []; let idle = false; const unsubscribe = session.on((event) => { if (event.agentId) return; + progress.push({ type: event.type, elapsedMs: Date.now() - startedAt }); + if (progress.length > 20) progress.shift(); if (event.type === "assistant.message") { replies.push(event); } else if (event.type === "session.error") { - errors.push(event.data.message); + errorCount++; + if (errors.length === 0) errors.push("session.error"); } else if (event.type === "session.idle") { idle = true; } @@ -320,6 +325,24 @@ describe("Structured output", async () => { expect(replies.some((event) => event.data.toolRequests?.length)).toBe(true); expect(reply.data.toolRequests ?? []).toEqual([]); expect(replies.at(-1)).toBe(reply); + } catch (error) { + console.error( + "Structured-output stop-hook diagnostics:", + JSON.stringify({ + sessionId: session.sessionId, + hookEntered, + idle, + errorCount, + errorCategories: errors, + progress, + replies: replies.slice(-20).map((reply) => ({ + originatingMessageId: reply.data.originatingMessageId, + toolRequestCount: reply.data.toolRequests?.length ?? 0, + contentLength: reply.data.content.length, + })), + }) + ); + throw error; } finally { releaseHook(); unsubscribe(); @@ -330,7 +353,7 @@ describe("Structured output", async () => { let stops = 0; const replies: AssistantMessageEvent[] = []; const schema = z.object({ answer: z.number().int() }); - const session = await client.createSession({ + await using session = await client.createSession({ model, provider, onPermissionRequest: approveAll, @@ -370,10 +393,9 @@ describe("Structured output", async () => { it("typed_wait_returns_late_steering_response", async () => { let stops = 0; let steeringId: string | undefined; - let session: CopilotSession; const replies: AssistantMessageEvent[] = []; const schema = z.object({ answer: z.number().int() }); - session = await client.createSession({ + await using session: CopilotSession = await client.createSession({ model, provider, onPermissionRequest: approveAll, @@ -423,7 +445,7 @@ describe("Structured output", async () => { const toolReleased = new Promise((resolve) => { releaseTool = resolve; }); - const session = await client.createSession({ + await using session = await client.createSession({ model, provider, onPermissionRequest: approveAll, @@ -480,7 +502,7 @@ describe("Structured output", async () => { }); it("sends_explicit_schema_for_message_and_batch", async () => { - const session = await client.createSession({ + await using session = await client.createSession({ model, provider, onPermissionRequest: approveAll, diff --git a/nodejs/test/e2e/subagent_prompt_resume.e2e.test.ts b/nodejs/test/e2e/subagent_prompt_resume.e2e.test.ts index 2e82470012..62faad1221 100644 --- a/nodejs/test/e2e/subagent_prompt_resume.e2e.test.ts +++ b/nodejs/test/e2e/subagent_prompt_resume.e2e.test.ts @@ -2,7 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ -import { writeFile } from "node:fs/promises"; +import { readFile, writeFile } from "node:fs/promises"; import { dirname, join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { describe, expect, it } from "vitest"; @@ -11,19 +11,24 @@ import type { CopilotSession, CustomAgentConfig, MCPStdioServerConfig, + SessionEvent, } from "../../src/index.js"; import { approveAll, CopilotRequestHandler } from "../../src/index.js"; import { createSdkTestContext } from "./harness/sdkTestContext.js"; import { waitForCondition } from "./harness/sdkTestHelper.js"; +import { isByokBackend } from "./harness/testBackend.js"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const TEST_MCP_SERVER = resolve(__dirname, "../../../test/harness/test-mcp-server.mjs"); const TEST_HARNESS_DIR = dirname(TEST_MCP_SERVER); +const capiIt = it.skipIf(isByokBackend); interface InferenceRequest { agentId?: string; parentAgentId?: string; + model: string; + reasoningEffort?: string; systemPrompt: string; toolNames: string[]; } @@ -37,9 +42,16 @@ class RecordingRequestHandler extends CopilotRequestHandler { ): Promise { const body = request.body ? await request.text() : ""; if (isInferenceUrl(request.url)) { + const inference = JSON.parse(body) as { + model: string; + reasoning_effort?: string; + reasoning?: { effort?: string }; + }; this.inferenceRequests.push({ agentId: ctx.agentId, parentAgentId: ctx.parentAgentId, + model: inference.model, + reasoningEffort: inference.reasoning_effort ?? inference.reasoning?.effort, systemPrompt: systemPromptFromRequest(request.url, body), toolNames: toolNamesFromRequest(body), }); @@ -122,6 +134,32 @@ function bootstrapResponse(url: string): Response { }, }, }, + { + id: "gpt-5.4-mini", + name: "GPT-5.4 Mini", + object: "model", + vendor: "OpenAI", + version: "1", + preview: false, + model_picker_enabled: true, + supported_endpoints: ["/chat/completions"], + capabilities: { + type: "chat", + family: "gpt-5.4-mini", + tokenizer: "o200k_base", + limits: { + max_context_window_tokens: 200000, + max_output_tokens: 8192, + }, + supports: { + streaming: true, + tool_calls: true, + parallel_tool_calls: true, + vision: true, + reasoning_effort: ["low", "medium", "high"], + }, + }, + }, ], }); } @@ -211,7 +249,11 @@ describe("Subagent prompt resume", async () => { const { copilotClient: client, env } = await createSdkTestContext({ copilotClientOptions: { requestHandler, - env: { COPILOT_EXP_COPILOT_CLI_SESSION_BASED_SUBAGENTS: "true" }, + env: { + COPILOT_EXP_COPILOT_CLI_SESSION_BASED_SUBAGENTS: "true", + STABILITY_ORDERED_SYSTEM_PROMPT_V2: "false", + COPILOT_EXP_COPILOT_CLI_STABILITY_ORDERED_SYSTEM_PROMPT_V2: "false", + }, }, }); @@ -225,14 +267,22 @@ describe("Subagent prompt resume", async () => { }, { timeoutMessage: `Agent ${agentId} did not become idle.` } ); + const task = (await session.rpc.tasks.list()).tasks.find( + (candidate) => candidate.id === agentId + ); + if (task?.type !== "agent") { + throw new Error(`Agent task ${agentId} is missing or has the wrong type.`); + } + expect(task.latestResponse ?? task.result).toContain("SUBAGENT_TURN_COMPLETE"); } async function runResumeScenario( trigger: (session: CopilotSession) => Promise, createOptions: Parameters[0] = {}, beforeAgentStart: (session: CopilotSession) => Promise = async () => {}, - agentType = "explore" - ): Promise<[InferenceRequest, InferenceRequest]> { + agentType = "explore", + agentModel: string | null = "claude-sonnet-5" + ): Promise<[InferenceRequest, InferenceRequest, string]> { await writeFile(join(env.COPILOT_HOME, "settings.json"), "{}"); const requestBaseline = requestHandler.inferenceRequests.length; const session = await client.createSession({ @@ -248,7 +298,7 @@ describe("Subagent prompt resume", async () => { prompt: "Reply with exactly SUBAGENT_TURN_COMPLETE.", name: "prompt-resume", description: "Exercise an idle subagent follow-up.", - model: "claude-sonnet-5", + ...(agentModel ? { model: agentModel } : {}), }); await waitForIdleAgent(session, agentId); @@ -270,12 +320,13 @@ describe("Subagent prompt resume", async () => { }, { timeoutMessage: "Agent did not complete its follow-up turn." } ); + await waitForIdleAgent(session, agentId); const childRequests = requestHandler.inferenceRequests .slice(requestBaseline) .filter((request) => request.agentId === agentId && request.parentAgentId); expect(childRequests).toHaveLength(2); - return [childRequests[0]!, childRequests[1]!]; + return [childRequests[0]!, childRequests[1]!, session.sessionId]; } finally { const tasks = await session.rpc.tasks.list(); for (const task of tasks.tasks) { @@ -311,12 +362,155 @@ describe("Subagent prompt resume", async () => { } it("preserves the adapted prompt on an ordinary idle-agent wake", async () => { - const [initial, resumed] = await runResumeScenario(async () => {}); + const [initial, resumed, sessionId] = await runResumeScenario(async () => {}); expectAdaptedExplorePrompt(initial.systemPrompt); expect(resumed.systemPrompt).toBe(initial.systemPrompt); + const journal = await readFile( + join(env.COPILOT_HOME, "session-state", sessionId, "events.jsonl"), + "utf8" + ); + const events = journal + .trim() + .split("\n") + .map((line) => JSON.parse(line) as SessionEvent); + const childEvents = events.filter((event) => event.agentId === initial.agentId); + const childPrompts = childEvents.filter((event) => event.type === "system.message"); + expect(childPrompts).toHaveLength(1); + expect( + childEvents + .filter((event) => event.type === "assistant.message") + .map((event) => event.data.content) + ).toEqual(["SUBAGENT_TURN_COMPLETE", "SUBAGENT_TURN_COMPLETE"]); + const ids = new Set(events.map((event) => event.id)); + for (const event of events) { + if (event.parentId) { + expect(ids.has(event.parentId), `Missing parent of ${event.id}`).toBe(true); + } + } + await using reopened = await client.resumeSession(sessionId, { + model: "claude-sonnet-5", + onPermissionRequest: approveAll, + }); + const history = await reopened.getEvents(); + expect(history.find((event) => event.id === childPrompts[0]!.id)).toEqual(childPrompts[0]); + }); + + it("keeps legacy prompt blocks live while persisting only their reconstructible content", async () => { + const content = + "sharedlimits\n\nper-user instructions"; + const systemMessage = { + mode: "replace" as const, + content, + contentBlocks: [ + { + content: "sharedlimits", + isStatic: true, + }, + { content: "per-user instructions" }, + ], + }; + await using session = await client.createSession({ + model: "claude-sonnet-5", + systemMessage, + onPermissionRequest: approveAll, + }); + const livePrompts: SessionEvent[] = []; + const unsubscribe = session.on("system.message", (event) => livePrompts.push(event)); + try { + const response = await session.sendAndWait({ + prompt: "Reply with exactly SUBAGENT_TURN_COMPLETE.", + }); + expect(response?.data.content).toBe("SUBAGENT_TURN_COMPLETE"); + } finally { + unsubscribe(); + } + await session.disconnect(); + const live = livePrompts.find((event) => event.type === "system.message"); + expect(live?.type).toBe("system.message"); + if (live?.type !== "system.message") { + throw new Error("Expected a live system prompt."); + } + expect(live.data.content).toBe(content); + expect(live.data.contentBlocks).toEqual(systemMessage.contentBlocks); + const journal = await readFile( + join(env.COPILOT_HOME, "session-state", session.sessionId, "events.jsonl"), + "utf8" + ); + const persisted = journal + .trim() + .split("\n") + .map((line) => JSON.parse(line) as SessionEvent) + .find((event) => event.id === live.id); + expect(persisted?.type).toBe("system.message"); + if (persisted?.type !== "system.message") { + throw new Error("Expected the persisted system prompt."); + } + expect(persisted.data.content).toBe(content); + expect(persisted.data.contentBlocks).toBeUndefined(); + await using reopened = await client.resumeSession(session.sessionId, { + model: "claude-sonnet-5", + onPermissionRequest: approveAll, + }); + const history = await reopened.getEvents(); + expect(history.find((event) => event.id === live.id)).toEqual(persisted); }); + capiIt.each(["minimal", "max", "xhigh", "none", "high"])( + "reconciles a concrete child's composed %s effort on its first and resumed requests", + async (effort) => { + const [initial, resumed] = await runResumeScenario( + async () => {}, + { + customAgents: [ + { + name: "composed-effort", + prompt: "Reply with exactly SUBAGENT_TURN_COMPLETE.", + model: `gpt-5.4-mini:defaultReasoningEffort=${effort}`, + tools: [], + }, + ], + }, + async () => {}, + "composed-effort", + null + ); + + for (const request of [initial, resumed]) { + expect(request.model).toBe("gpt-5.4-mini"); + expect(request.reasoningEffort).toBe(effort === "high" ? "high" : "medium"); + } + } + ); + + capiIt( + "reconciles an independently declared child effort without changing its model", + async () => { + const [initial, resumed] = await runResumeScenario( + async () => {}, + { + customAgents: [ + { + name: "declared-effort", + prompt: "Reply with exactly SUBAGENT_TURN_COMPLETE.", + model: "gpt-5.4-mini", + reasoningEffort: "max", + tools: [], + }, + ], + }, + async () => {}, + "declared-effort", + null + ); + + for (const request of [initial, resumed]) { + expect(request.model).toBe("gpt-5.4-mini"); + expect(request.reasoningEffort).toBe("medium"); + } + } + ); + it("preserves the adapted prompt after persisted settings rebuild the child context", async () => { const [initial, resumed] = await runResumeScenario(changePersistedSettings); diff --git a/nodejs/test/e2e/telemetry.e2e.test.ts b/nodejs/test/e2e/telemetry.e2e.test.ts index 6d2915744e..3c6aecda89 100644 --- a/nodejs/test/e2e/telemetry.e2e.test.ts +++ b/nodejs/test/e2e/telemetry.e2e.test.ts @@ -186,6 +186,11 @@ describe("Telemetry export", async () => { `{"value":"${marker}"}` ); expect(getStringAttribute(toolSpan!, "gen_ai.tool.call.result")).toBe(marker); + expect(toolSpan!.attributes).not.toHaveProperty( + "github.copilot.tool.parameters.mcp_server_name_hash" + ); + expect(toolSpan!.attributes).not.toHaveProperty("github.copilot.mcp.server.transport"); + expect(toolSpan!.attributes).not.toHaveProperty("github.copilot.mcp.server.source"); }); it("should export per request subagent chat spans", { timeout: 90_000 }, async () => { diff --git a/nodejs/test/e2e/telemetry_preparation.e2e.test.ts b/nodejs/test/e2e/telemetry_preparation.e2e.test.ts new file mode 100644 index 0000000000..8c8f79c5a1 --- /dev/null +++ b/nodejs/test/e2e/telemetry_preparation.e2e.test.ts @@ -0,0 +1,170 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { readFile, writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import { describe, it, onTestFinished } from "vitest"; +import { z } from "zod"; +import { + type CopilotSession, + defineTool, + type PermissionRequestResult, + RuntimeConnection, + type SessionEvent, +} from "../../src/index.js"; +import { + createSdkTestContext, + DEFAULT_GITHUB_TOKEN, + getLegacyCliPathForTests, +} from "./harness/sdkTestContext.js"; + +async function telemetryWarnings(session: CopilotSession) { + return (await session.getEvents()).filter( + (event) => event.type === "session.warning" && event.data.warningType === "telemetry" + ); +} + +describe.each(["native", "legacy"] as const)("Telemetry preparation (%s)", async (host) => { + const policyFile = "managed-telemetry-policy.json"; + const telemetryFile = "session-telemetry.jsonl"; + const harness = await createSdkTestContext({ + copilotClientOptions: { + connection: RuntimeConnection.forStdio(), + env: { + COPILOT_E2E_TEST_HOOKS: "1", + COPILOT_TEST_MANAGED_SETTINGS_FILE_PATH: policyFile, + COPILOT_MANAGED_SETTINGS_CACHE: "false", + }, + }, + }); + + it("keeps create and resume usable after exporter failure", async ({ expect }) => { + await writeFile( + join(harness.workDir, policyFile), + JSON.stringify({ + telemetry: { + enabled: true, + endpoint: "ftp://collector.example", + headers: { "x-routing-key": "managed-value" }, + }, + }) + ); + const options = { + gitHubToken: DEFAULT_GITHUB_TOKEN, + connection: RuntimeConnection.forStdio({ + path: + host === "legacy" + ? await getLegacyCliPathForTests() + : process.env.COPILOT_CLI_PATH, + }), + telemetry: { + exporterType: "file" as const, + filePath: telemetryFile, + sourceName: "ts-sdk-telemetry-e2e", + captureContent: true, + }, + }; + const client = harness.createClient(options); + onTestFinished(() => client.stop()); + const receivedWarnings: SessionEvent[] = []; + const sessionOptions = { + enableManagedSettings: true, + onEvent: (event: SessionEvent) => { + if (event.type === "session.warning" && event.data.warningType === "telemetry") { + receivedWarnings.push(event); + } + }, + onPermissionRequest: (): PermissionRequestResult => ({ kind: "approve-once" }), + tools: [ + defineTool("echo_telemetry_marker", { + description: "Echoes a marker string for telemetry validation.", + parameters: z.object({ value: z.string() }), + handler: ({ value }) => value, + }), + ], + }; + const session = await client.createSession(sessionOptions); + const failedSessionId = session.sessionId; + const initialWarnings = await telemetryWarnings(session); + expect(initialWarnings).toMatchObject([ + { + data: { + warningType: "telemetry", + message: + "OpenTelemetry could not be initialized and is disabled for this session. You can continue using the session.", + }, + }, + ]); + const replay = { + filePath: join( + import.meta.dirname, + "..", + "..", + "..", + "test", + "snapshots", + "telemetry", + "should_export_file_telemetry_for_sdk_interactions.yaml" + ), + workDir: harness.workDir, + }; + const prompt = + "Use the echo_telemetry_marker tool with value 'copilot-sdk-telemetry-e2e', then respond with TELEMETRY_E2E_DONE."; + await harness.openAiEndpoint.updateConfig(replay); + const assistant = await session.sendAndWait({ prompt }, 90_000); + expect(assistant?.data.content).toContain("TELEMETRY_E2E_DONE"); + expect(receivedWarnings.map((event) => event.id)).toEqual( + initialWarnings.map((event) => event.id) + ); + await client.rpc.sessions.save({ sessionId: failedSessionId }); + const resident = await client.resumeSession(failedSessionId, sessionOptions); + expect(resident.sessionId).toBe(failedSessionId); + await expect(resident.rpc.model.getCurrent()).resolves.toHaveProperty("modelId"); + expect(await telemetryWarnings(resident)).toHaveLength(1); + await resident.disconnect(); + await session.disconnect(); + expect(await client.stop()).toEqual([]); + + const resumedClient = harness.createClient(options); + onTestFinished(() => resumedClient.stop()); + const cold = await resumedClient.resumeSession(failedSessionId, sessionOptions); + expect(cold.sessionId).toBe(failedSessionId); + await expect(cold.rpc.model.getCurrent()).resolves.toHaveProperty("modelId"); + expect(await telemetryWarnings(cold)).toHaveLength(1); + await cold.disconnect(); + + await writeFile(join(harness.workDir, policyFile), "{}"); + let healthyClient = resumedClient; + if (host === "legacy") { + // The legacy host retains its startup device policy until the next process. + expect(await resumedClient.stop()).toEqual([]); + healthyClient = harness.createClient(options); + onTestFinished(() => healthyClient.stop()); + } + const healthy = await healthyClient.createSession(sessionOptions); + await harness.openAiEndpoint.updateConfig(replay); + const healthyAssistant = await healthy.sendAndWait({ prompt }, 90_000); + expect(healthyAssistant?.data.content).toContain("TELEMETRY_E2E_DONE"); + expect(await telemetryWarnings(healthy)).toEqual([]); + await healthy.disconnect(); + expect(await healthyClient.stop()).toEqual([]); + + const entries: Array<{ attributes?: Record }> = ( + await readFile(join(harness.workDir, telemetryFile), "utf8") + ) + .split(/\r?\n/) + .filter(Boolean) + .map((line) => JSON.parse(line)); + expect( + entries.some( + (entry) => entry.attributes?.["gen_ai.conversation.id"] === healthy.sessionId + ) + ).toBe(true); + expect( + entries.some( + (entry) => entry.attributes?.["gen_ai.conversation.id"] === failedSessionId + ) + ).toBe(false); + }); +}); diff --git a/nodejs/test/e2e/tools_anthropic_updates.e2e.test.ts b/nodejs/test/e2e/tools_anthropic_updates.e2e.test.ts index 721b432ae1..71985070d7 100644 --- a/nodejs/test/e2e/tools_anthropic_updates.e2e.test.ts +++ b/nodejs/test/e2e/tools_anthropic_updates.e2e.test.ts @@ -2,15 +2,19 @@ * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ +import { writeFile } from "node:fs/promises"; +import { join } from "node:path"; import { describe, expect, it } from "vitest"; import { approveAll, CopilotRequestHandler, defineTool, type CopilotRequestContext, + type SessionConfig, } from "../../src/index.js"; import { createSdkTestContext } from "./harness/sdkTestContext.js"; import { waitForCondition } from "./harness/sdkTestHelper.js"; +import { mcpTestServerConfig } from "./harness/mcpTestServer.js"; interface AnthropicRequest { system: unknown; @@ -110,7 +114,11 @@ class AnthropicToolUpdateHandler extends CopilotRequestHandler { readonly requests: Array<{ body: AnthropicRequest; beta: string | null }> = []; constructor( - private readonly rejectPositionedUpdate: false | "before-output" | "after-output" = false + private readonly rejectPositionedUpdate: false | "before-output" | "after-output" = false, + private readonly addedTool: { name: string; input: Record } = { + name: "sdk_epoch_added", + input: {}, + } ) { super(); } @@ -119,7 +127,28 @@ class AnthropicToolUpdateHandler extends CopilotRequestHandler { request: Request, ctx: CopilotRequestContext ): Promise { - if (new URL(request.url).hostname !== "api.anthropic.com") { + const url = new URL(request.url); + if (url.pathname === "/models") { + return Response.json({ + data: [ + { + id: "claude-opus-5.5", + name: "Claude Opus 5.5", + vendor: "Anthropic", + model_picker_enabled: true, + supported_endpoints: ["/v1/messages"], + capabilities: { + type: "chat", + family: "claude-opus-5.5", + tokenizer: "o200k_base", + limits: { max_context_window_tokens: 200000, max_output_tokens: 32000 }, + supports: { streaming: true, tool_calls: true }, + }, + }, + ], + }); + } + if (!url.pathname.endsWith("/messages")) { return super.sendRequest(request, ctx); } const body = (await request.json()) as AnthropicRequest; @@ -133,6 +162,27 @@ class AnthropicToolUpdateHandler extends CopilotRequestHandler { } this.requests.push({ body, beta: request.headers.get("anthropic-beta") }); + if ( + body.messages.some( + (message) => + message.role !== "system" && + Array.isArray(message.content) && + message.content.some((block: { type?: string }) => + ["tool_addition", "tool_removal"].includes(block.type ?? "") + ) + ) + ) { + return Response.json( + { + error: { + type: "invalid_request_error", + message: "tool_addition/tool_removal require role: system", + }, + }, + { status: 400 } + ); + } + if (serialized.includes("SDK_ANTHROPIC_FOLLOW_UP")) { return streamResponse({ type: "text", text: "SDK_ANTHROPIC_LEGACY_OK" }, "end_turn"); } @@ -167,7 +217,7 @@ class AnthropicToolUpdateHandler extends CopilotRequestHandler { ); } return streamResponse( - { type: "tool_use", id: "toolu_sdk_added", name: "sdk_epoch_added", input: {} }, + { type: "tool_use", id: "toolu_sdk_added", ...this.addedTool }, "tool_use" ); } @@ -181,6 +231,426 @@ describe("SDK tool updates on direct Anthropic Messages", async () => { copilotClientOptions: { requestHandler: handler }, }); + describe("MCP tool updates on direct Anthropic Messages", async () => { + const toolName = "a_server-get_env"; + const handler = new AnthropicToolUpdateHandler(false, { + name: toolName, + input: { name: "CACHE_TEST_RESULT" }, + }); + const { copilotClient: client, workDir } = await createSdkTestContext({ + copilotClientOptions: { requestHandler: handler }, + }); + + const routes: Array<{ name: string; config: Partial }> = [ + { + name: "direct BYOK", + config: { + provider: { + type: "anthropic", + baseUrl: "https://api.anthropic.com", + apiKey: "synthetic-provider-key", + modelId: "claude-opus-5.5", + wireModel: "claude-opus-5.5", + }, + }, + }, + { + name: "compatible BYOK proxy", + config: { + provider: { + type: "anthropic", + baseUrl: "https://messages.example.test", + apiKey: "synthetic-provider-key", + modelId: "claude-opus-5.5", + wireModel: "claude-opus-5.5", + }, + }, + }, + { name: "CAPI", config: {} }, + ]; + for (const route of routes) { + it(`keeps the serialized prefix on ${route.name} when enabling an MCP server adds tools before existing ones`, async () => { + const config = mcpTestServerConfig("SDK_ADDED_TOOL_RESULT"); + const beforeRequests = handler.requests.length; + const session = await client.createSession({ + model: "claude-opus-5.5", + toolSearch: { enabled: false }, + onPermissionRequest: approveAll, + ...route.config, + mcpServers: { z_server: config, a_server: config }, + disabledMcpServers: ["a_server"], + }); + try { + await session.rpc.tools.initializeAndValidate(); + expect( + (await session.rpc.tools.getCurrentMetadata()).tools.map( + (tool) => tool.name + ) + ).toContain("z_server-get_env"); + expect( + (await session.rpc.tools.getCurrentMetadata()).tools.map( + (tool) => tool.name + ) + ).not.toContain(toolName); + const first = await session.sendAndWait({ prompt: "SDK_ANTHROPIC_FIRST" }); + expect(first?.data.content).toContain("SDK_ANTHROPIC_FIRST_OK"); + + await session.rpc.mcp.setEnvValueMode({ mode: "direct" }); + await session.rpc.mcp.enable({ serverName: "a_server" }); + await session.rpc.tools.initializeAndValidate(); + expect( + (await session.rpc.tools.getCurrentMetadata()).tools.map( + (tool) => tool.name + ) + ).toContain(toolName); + const second = await session.sendAndWait({ + prompt: `SDK_ANTHROPIC_AFTER: call ${toolName}.`, + }); + expect(second?.data.content).toContain("SDK_ANTHROPIC_TOOL_OK"); + + const requests = handler.requests.slice(beforeRequests); + expect(requests).toHaveLength(3); + const [before, update, afterTool] = requests; + expect(before.body.tools).toEqual( + expect.arrayContaining([ + expect.objectContaining({ name: "z_server-get_env" }), + ]) + ); + expect(JSON.stringify(before.body.tools)).not.toContain(`"name":"${toolName}"`); + for (const request of [update, afterTool]) { + expect(JSON.stringify(request.body.system)).toBe( + JSON.stringify(before.body.system) + ); + expect(JSON.stringify(request.body.tools)).toBe( + JSON.stringify(before.body.tools) + ); + } + expect(update.body.messages).toContainEqual( + expect.objectContaining({ + role: "system", + content: [ + expect.objectContaining({ + type: "tool_addition", + tool: expect.objectContaining({ + type: "tool_definition", + definition: expect.objectContaining({ name: toolName }), + }), + }), + ], + }) + ); + expect(update.beta).toContain("inline-tools-2026-09-15"); + expect(JSON.stringify(afterTool.body.messages)).toContain( + "SDK_ADDED_TOOL_RESULT" + ); + expect( + (await session.rpc.tools.getCurrentMetadata()).tools.map( + (tool) => tool.name + ) + ).toEqual(expect.arrayContaining(["a_server-get_env", "z_server-get_env"])); + } finally { + await session.disconnect(); + } + }); + } + + describe("CAPI Messages compatibility", async () => { + const handler = new AnthropicToolUpdateHandler(); + const { copilotClient: client } = await createSdkTestContext({ + copilotClientOptions: { requestHandler: handler }, + }); + it("preserves the prefix across CAPI reordering, removal and restoration without a rejected attempt", async () => { + let addedCalls = 0; + const original = { + name: "sdk_epoch_original", + description: "Original SDK tool", + parameters: { type: "object", properties: {} }, + defer: "never", + } as const; + const added = { + ...original, + name: "sdk_epoch_added", + description: "Added SDK tool", + }; + const session = await client.createSession({ + model: "claude-opus-5.5", + toolSearch: { enabled: false }, + onPermissionRequest: approveAll, + availableTools: [original.name, added.name], + tools: [ + defineTool(original.name, { + description: original.description, + handler: () => "original", + }), + defineTool(added.name, { + description: added.description, + handler: () => { + addedCalls++; + return "SDK_ADDED_TOOL_RESULT"; + }, + }), + ], + }); + try { + await session.rpc.tools.initializeAndValidate(); + await session.rpc.tools.set({ tools: [original, added] }); + expect( + (await session.sendAndWait({ prompt: "SDK_ANTHROPIC_FIRST" }))?.data.content + ).toContain("SDK_ANTHROPIC_FIRST_OK"); + await session.rpc.tools.set({ tools: [added, original] }); + expect( + (await session.sendAndWait({ prompt: "SDK_ANTHROPIC_FIRST_REORDERED" })) + ?.data.content + ).toContain("SDK_ANTHROPIC_FIRST_OK"); + await session.rpc.tools.set({ tools: [original] }); + expect( + (await session.sendAndWait({ prompt: "SDK_ANTHROPIC_FIRST_REMOVED" }))?.data + .content + ).toContain("SDK_ANTHROPIC_FIRST_OK"); + await session.rpc.tools.set({ tools: [added, original] }); + expect( + ( + await session.sendAndWait({ + prompt: "SDK_ANTHROPIC_AFTER: call sdk_epoch_added.", + }) + )?.data.content + ).toContain("SDK_ANTHROPIC_TOOL_OK"); + expect(addedCalls).toBe(1); + expect(handler.requests).toHaveLength(5); + const [before, reordered, removed, restored, afterTool] = handler.requests; + expect(JSON.stringify(reordered.body.tools)).toBe( + JSON.stringify(before.body.tools) + ); + expect(removed.body.messages).toContainEqual({ + role: "system", + content: [ + { + type: "tool_removal", + tool: { type: "tool_reference", name: added.name }, + }, + ], + }); + expect(removed.beta).toContain("mid-conversation-tool-changes-2026-07-01"); + expect(restored.body.messages).toContainEqual( + expect.objectContaining({ + role: "system", + content: [ + expect.objectContaining({ + type: "tool_addition", + tool: expect.objectContaining({ + definition: expect.objectContaining({ name: added.name }), + }), + }), + ], + }) + ); + expect(restored.beta).toContain("inline-tools-2026-09-15"); + expect(JSON.stringify(afterTool.body.messages)).toContain( + "SDK_ADDED_TOOL_RESULT" + ); + for (const request of handler.requests) { + expect(JSON.stringify(request.body.system)).toBe( + JSON.stringify(before.body.system) + ); + expect(JSON.stringify(request.body.tools)).toBe( + JSON.stringify(before.body.tools) + ); + } + } finally { + await session.disconnect(); + } + }); + }); + + it.each(routes)( + "keeps the prefix across MCP removal and restoration on $name and executes the restored tool", + async (route) => { + const config = mcpTestServerConfig("SDK_ADDED_TOOL_RESULT"); + const beforeRequests = handler.requests.length; + const session = await client.createSession({ + model: "claude-opus-5.5", + toolSearch: { enabled: false }, + onPermissionRequest: approveAll, + availableTools: [toolName, "z_server-get_env"], + ...route.config, + mcpServers: { a_server: config, z_server: config }, + }); + try { + await session.rpc.mcp.setEnvValueMode({ mode: "direct" }); + await session.rpc.tools.initializeAndValidate(); + expect( + (await session.rpc.tools.getCurrentMetadata()).tools.map( + (tool) => tool.name + ) + ).toEqual(expect.arrayContaining([toolName, "z_server-get_env"])); + expect( + (await session.sendAndWait({ prompt: "SDK_ANTHROPIC_FIRST" }))?.data.content + ).toContain("SDK_ANTHROPIC_FIRST_OK"); + + await session.rpc.mcp.disable({ serverName: "a_server" }); + await session.rpc.tools.initializeAndValidate(); + // Tool metadata includes the previous request; the host reports live availability. + const disabled = await session.rpc.mcp.list(); + expect(disabled.host?.disabledServers).toContain("a_server"); + expect(disabled.host?.disabledServers).not.toContain("z_server"); + expect(disabled.servers).toContainEqual( + expect.objectContaining({ name: "a_server", status: "disabled" }) + ); + expect( + (await session.sendAndWait({ prompt: "SDK_ANTHROPIC_FIRST_REMOVED" }))?.data + .content + ).toContain("SDK_ANTHROPIC_FIRST_OK"); + + await session.rpc.mcp.enable({ serverName: "a_server" }); + await session.rpc.tools.initializeAndValidate(); + expect( + (await session.rpc.tools.getCurrentMetadata()).tools.map( + (tool) => tool.name + ) + ).toContain(toolName); + expect( + ( + await session.sendAndWait({ + prompt: `SDK_ANTHROPIC_AFTER: call ${toolName}.`, + }) + )?.data.content + ).toContain("SDK_ANTHROPIC_TOOL_OK"); + + const requests = handler.requests.slice(beforeRequests); + expect(requests).toHaveLength(4); + const [before, removed, restored, afterTool] = requests; + expect(before.body.tools).toEqual( + expect.arrayContaining([expect.objectContaining({ name: toolName })]) + ); + for (const request of [removed, restored, afterTool]) { + expect(JSON.stringify(request.body.system)).toBe( + JSON.stringify(before.body.system) + ); + expect(JSON.stringify(request.body.tools)).toBe( + JSON.stringify(before.body.tools) + ); + } + const blocks = (request: AnthropicRequest) => + request.messages.flatMap((message) => + Array.isArray(message.content) ? message.content : [] + ); + expect(blocks(removed.body)).toContainEqual({ + type: "tool_removal", + tool: { type: "tool_reference", name: toolName }, + }); + expect(removed.beta).toContain("mid-conversation-tool-changes-2026-07-01"); + expect(restored.beta).toContain("inline-tools-2026-09-15"); + expect( + blocks(restored.body) + .filter((block) => + ["tool_removal", "tool_addition"].includes(block.type) + ) + .map((block) => block.type) + ).toEqual(["tool_removal", "tool_addition"]); + expect(blocks(restored.body)).toContainEqual( + expect.objectContaining({ + type: "tool_addition", + tool: expect.objectContaining({ + definition: expect.objectContaining({ name: toolName }), + }), + }) + ); + expect(JSON.stringify(afterTool.body.messages)).toContain( + "SDK_ADDED_TOOL_RESULT" + ); + } finally { + await session.disconnect(); + } + } + ); + + it.each(routes)( + "replaces a refreshed MCP definition inline on $name and executes the restarted server", + async (route) => { + const descriptionFile = join(workDir, `tool-description-${route.name}.txt`); + await writeFile(descriptionFile, "MCP_ORIGINAL_DESCRIPTION"); + const beforeRequests = handler.requests.length; + const session = await client.createSession({ + model: "claude-opus-5.5", + toolSearch: { enabled: false }, + onPermissionRequest: approveAll, + ...route.config, + mcpServers: { + a_server: mcpTestServerConfig("SDK_ADDED_TOOL_RESULT", descriptionFile), + }, + }); + try { + await session.rpc.mcp.setEnvValueMode({ mode: "direct" }); + await session.rpc.tools.initializeAndValidate(); + expect( + (await session.rpc.tools.getCurrentMetadata()).tools.find( + (tool) => tool.name === toolName + )?.description + ).toContain("MCP_ORIGINAL_DESCRIPTION"); + expect( + (await session.sendAndWait({ prompt: "SDK_ANTHROPIC_FIRST" }))?.data.content + ).toContain("SDK_ANTHROPIC_FIRST_OK"); + + await writeFile(descriptionFile, "MCP_REPLACEMENT_DESCRIPTION"); + await session.rpc.mcp.restartServer({ serverName: "a_server" }); + await session.rpc.tools.initializeAndValidate(); + expect( + (await session.rpc.tools.getCurrentMetadata()).tools.find( + (tool) => tool.name === toolName + )?.description + ).toContain("MCP_REPLACEMENT_DESCRIPTION"); + expect( + ( + await session.sendAndWait({ + prompt: `SDK_ANTHROPIC_AFTER: call ${toolName}.`, + }) + )?.data.content + ).toContain("SDK_ANTHROPIC_TOOL_OK"); + + const requests = handler.requests.slice(beforeRequests); + expect(requests).toHaveLength(3); + const [before, update, afterTool] = requests; + expect(before.body.tools).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: toolName, + description: expect.stringContaining("MCP_ORIGINAL_DESCRIPTION"), + }), + ]) + ); + for (const request of [update, afterTool]) { + expect(JSON.stringify(request.body.system)).toBe( + JSON.stringify(before.body.system) + ); + expect(JSON.stringify(request.body.tools)).toBe( + JSON.stringify(before.body.tools) + ); + } + const additions = update.body.messages + .flatMap((message) => + Array.isArray(message.content) ? message.content : [] + ) + .filter((block) => block.type === "tool_addition"); + expect(additions).toHaveLength(1); + expect(additions[0]).toMatchObject({ + tool: { + definition: { + name: toolName, + description: expect.stringContaining("MCP_REPLACEMENT_DESCRIPTION"), + }, + }, + }); + expect(update.beta).toContain("inline-tools-2026-09-15"); + expect(JSON.stringify(afterTool.body.messages)).toContain( + "SDK_ADDED_TOOL_RESULT" + ); + } finally { + await session.disconnect(); + } + } + ); + }); + it("keeps the top-level prefix and executes a newly added inline tool", async () => { let addedToolCalls = 0; const session = await client.createSession({ @@ -263,149 +733,169 @@ describe("SDK tool updates on direct Anthropic Messages", async () => { }); }); -describe("SDK tool updates on direct Anthropic Messages", async () => { - const rejectingHandler = new AnthropicToolUpdateHandler("before-output"); - const { copilotClient: retryClient } = await createSdkTestContext({ - copilotClientOptions: { requestHandler: rejectingHandler }, - }); - - it("retries a rejected inline update once with the full current tools", async () => { - const original = { - name: "sdk_epoch_original", - description: "Original SDK tool", - parameters: { type: "object", properties: {} }, - defer: "never", - } as const; - const added = { - name: "sdk_epoch_added", - description: "Added SDK tool", - parameters: { type: "object", properties: {} }, - defer: "never", - } as const; - const session = await retryClient.createSession({ - model: "claude-opus-5.5", - toolSearch: { enabled: false }, - onPermissionRequest: approveAll, - provider: { - type: "anthropic", - baseUrl: "https://api.anthropic.com", - apiKey: "synthetic-provider-key", - modelId: "claude-opus-5.5", - wireModel: "claude-opus-5.5", - }, - tools: [ - defineTool(original.name, { - description: original.description, - handler: () => "original", - }), - defineTool(added.name, { description: added.description, handler: () => "added" }), - ], +describe.each(["BYOK", "CAPI"])( + "SDK tool update recovery on %s Anthropic Messages", + async (route) => { + const rejectingHandler = new AnthropicToolUpdateHandler("before-output"); + const { copilotClient: retryClient } = await createSdkTestContext({ + copilotClientOptions: { requestHandler: rejectingHandler }, }); - try { - await session.rpc.tools.initializeAndValidate(); - await session.rpc.tools.set({ tools: [original] }); - const first = await session.sendAndWait({ prompt: "SDK_ANTHROPIC_FIRST" }); - expect(first?.data.content).toContain("SDK_ANTHROPIC_FIRST_OK"); - await session.rpc.tools.set({ tools: [original, added] }); - const recovered = await session.sendAndWait({ - prompt: "SDK_ANTHROPIC_AFTER: add a tool.", + it("retries a rejected inline update once with the full current tools", async () => { + const original = { + name: "sdk_epoch_original", + description: "Original SDK tool", + parameters: { type: "object", properties: {} }, + defer: "never", + } as const; + const added = { + name: "sdk_epoch_added", + description: "Added SDK tool", + parameters: { type: "object", properties: {} }, + defer: "never", + } as const; + const session = await retryClient.createSession({ + model: "claude-opus-5.5", + toolSearch: { enabled: false }, + onPermissionRequest: approveAll, + provider: + route === "BYOK" + ? { + type: "anthropic", + baseUrl: "https://api.anthropic.com", + apiKey: "synthetic-provider-key", + modelId: "claude-opus-5.5", + wireModel: "claude-opus-5.5", + } + : undefined, + tools: [ + defineTool(original.name, { + description: original.description, + handler: () => "original", + }), + defineTool(added.name, { + description: added.description, + handler: () => "added", + }), + ], }); - expect(recovered?.data.content).toContain("SDK_ANTHROPIC_RECOVERED"); - expect(rejectingHandler.requests).toHaveLength(3); - const [before, rejected, retry] = rejectingHandler.requests; - expect(rejected.body.system).toEqual(before.body.system); - expect(rejected.body.tools).toEqual(before.body.tools); - expect(JSON.stringify(rejected.body.messages)).toContain('"type":"tool_addition"'); - expect(rejected.beta).toContain("inline-tools-2026-09-15"); - expect(retry.body.system).toEqual(before.body.system); - expect((retry.body.tools as Array<{ name: string }>).map((tool) => tool.name)).toEqual( - expect.arrayContaining([original.name, added.name]) - ); - expect(JSON.stringify(retry.body.messages)).not.toContain('"type":"tool_addition"'); - expect(retry.beta).not.toContain("inline-tools-2026-09-15"); - } finally { - await session.disconnect(); - } - }); -}); - -describe("SDK tool updates after a streamed Anthropic rejection", async () => { - const handler = new AnthropicToolUpdateHandler("after-output"); - const { copilotClient: client } = await createSdkTestContext({ - copilotClientOptions: { requestHandler: handler }, - }); + try { + await session.rpc.tools.initializeAndValidate(); + await session.rpc.tools.set({ tools: [original] }); + const first = await session.sendAndWait({ prompt: "SDK_ANTHROPIC_FIRST" }); + expect(first?.data.content).toContain("SDK_ANTHROPIC_FIRST_OK"); - it("disables the rejected epoch without replaying output and uses full tools next turn", async () => { - const original = { - name: "sdk_epoch_original", - description: "Original SDK tool", - parameters: { type: "object", properties: {} }, - defer: "never", - } as const; - const added = { - name: "sdk_epoch_added", - description: "Added SDK tool", - parameters: { type: "object", properties: {} }, - defer: "never", - } as const; - const session = await client.createSession({ - model: "claude-opus-5.5", - toolSearch: { enabled: false }, - onPermissionRequest: approveAll, - provider: { - type: "anthropic", - baseUrl: "https://api.anthropic.com", - apiKey: "synthetic-provider-key", - modelId: "claude-opus-5.5", - wireModel: "claude-opus-5.5", - }, - tools: [ - defineTool(original.name, { - description: original.description, - handler: () => "original", - }), - defineTool(added.name, { description: added.description, handler: () => "added" }), - ], - }); - let failedTurnIdle = false; - const unsubscribe = session.on((event) => { - if (event.type === "session.idle" && !event.agentId) { - failedTurnIdle = true; + await session.rpc.tools.set({ tools: [original, added] }); + const recovered = await session.sendAndWait({ + prompt: "SDK_ANTHROPIC_AFTER: add a tool.", + }); + expect(recovered?.data.content).toContain("SDK_ANTHROPIC_RECOVERED"); + expect(rejectingHandler.requests).toHaveLength(3); + const [before, rejected, retry] = rejectingHandler.requests; + expect(rejected.body.system).toEqual(before.body.system); + expect(rejected.body.tools).toEqual(before.body.tools); + expect(JSON.stringify(rejected.body.messages)).toContain('"type":"tool_addition"'); + expect(rejected.beta).toContain("inline-tools-2026-09-15"); + expect(retry.body.system).toEqual(before.body.system); + expect( + (retry.body.tools as Array<{ name: string }>).map((tool) => tool.name) + ).toEqual(expect.arrayContaining([original.name, added.name])); + expect(JSON.stringify(retry.body.messages)).not.toContain('"type":"tool_addition"'); + expect(retry.beta).not.toContain("inline-tools-2026-09-15"); + } finally { + await session.disconnect(); } }); - try { - await session.rpc.tools.initializeAndValidate(); - await session.rpc.tools.set({ tools: [original] }); - const first = await session.sendAndWait({ prompt: "SDK_ANTHROPIC_FIRST" }); - expect(first?.data.content).toContain("SDK_ANTHROPIC_FIRST_OK"); - failedTurnIdle = false; + } +); - await session.rpc.tools.set({ tools: [original, added] }); - await expect( - session.sendAndWait({ prompt: "SDK_ANTHROPIC_AFTER: add a tool." }) - ).rejects.toThrow(/invalid tool_addition/); - await waitForCondition(() => failedTurnIdle, { - timeoutMessage: "Rejected SDK tool update did not reach session.idle", +describe.each(["BYOK", "CAPI"])( + "SDK tool updates after a streamed %s Anthropic rejection", + async (route) => { + const handler = new AnthropicToolUpdateHandler("after-output"); + const { copilotClient: client } = await createSdkTestContext({ + copilotClientOptions: { requestHandler: handler }, + }); + + it("disables the rejected epoch without replaying output and uses full tools next turn", async () => { + const original = { + name: "sdk_epoch_original", + description: "Original SDK tool", + parameters: { type: "object", properties: {} }, + defer: "never", + } as const; + const added = { + name: "sdk_epoch_added", + description: "Added SDK tool", + parameters: { type: "object", properties: {} }, + defer: "never", + } as const; + const session = await client.createSession({ + model: "claude-opus-5.5", + toolSearch: { enabled: false }, + onPermissionRequest: approveAll, + provider: + route === "BYOK" + ? { + type: "anthropic", + baseUrl: "https://api.anthropic.com", + apiKey: "synthetic-provider-key", + modelId: "claude-opus-5.5", + wireModel: "claude-opus-5.5", + } + : undefined, + tools: [ + defineTool(original.name, { + description: original.description, + handler: () => "original", + }), + defineTool(added.name, { + description: added.description, + handler: () => "added", + }), + ], }); - expect(handler.requests).toHaveLength(2); - const [before, rejected] = handler.requests; - expect(rejected.body.system).toEqual(before.body.system); - expect(rejected.body.tools).toEqual(before.body.tools); - expect(JSON.stringify(rejected.body.messages)).toContain('"type":"tool_addition"'); - - const followUp = await session.sendAndWait({ prompt: "SDK_ANTHROPIC_FOLLOW_UP" }); - expect(followUp?.data.content).toContain("SDK_ANTHROPIC_LEGACY_OK"); - expect(handler.requests).toHaveLength(3); - const request = handler.requests[2]; - expect( - (request.body.tools as Array<{ name: string }>).map((tool) => tool.name) - ).toEqual(expect.arrayContaining([original.name, added.name])); - expect(JSON.stringify(request.body.messages)).not.toContain('"type":"tool_addition"'); - expect(request.beta).not.toContain("inline-tools-2026-09-15"); - } finally { - unsubscribe(); - await session.disconnect(); - } - }); -}); + let failedTurnIdle = false; + const unsubscribe = session.on((event) => { + if (event.type === "session.idle" && !event.agentId) { + failedTurnIdle = true; + } + }); + try { + await session.rpc.tools.initializeAndValidate(); + await session.rpc.tools.set({ tools: [original] }); + const first = await session.sendAndWait({ prompt: "SDK_ANTHROPIC_FIRST" }); + expect(first?.data.content).toContain("SDK_ANTHROPIC_FIRST_OK"); + failedTurnIdle = false; + + await session.rpc.tools.set({ tools: [original, added] }); + await expect( + session.sendAndWait({ prompt: "SDK_ANTHROPIC_AFTER: add a tool." }) + ).rejects.toThrow(/invalid tool_addition/); + await waitForCondition(() => failedTurnIdle, { + timeoutMessage: "Rejected SDK tool update did not reach session.idle", + }); + expect(handler.requests).toHaveLength(2); + const [before, rejected] = handler.requests; + expect(rejected.body.system).toEqual(before.body.system); + expect(rejected.body.tools).toEqual(before.body.tools); + expect(JSON.stringify(rejected.body.messages)).toContain('"type":"tool_addition"'); + + const followUp = await session.sendAndWait({ prompt: "SDK_ANTHROPIC_FOLLOW_UP" }); + expect(followUp?.data.content).toContain("SDK_ANTHROPIC_LEGACY_OK"); + expect(handler.requests).toHaveLength(3); + const request = handler.requests[2]; + expect( + (request.body.tools as Array<{ name: string }>).map((tool) => tool.name) + ).toEqual(expect.arrayContaining([original.name, added.name])); + expect(JSON.stringify(request.body.messages)).not.toContain( + '"type":"tool_addition"' + ); + expect(request.beta).not.toContain("inline-tools-2026-09-15"); + } finally { + unsubscribe(); + await session.disconnect(); + } + }); + } +); diff --git a/nodejs/test/e2e/user_settings_edit.e2e.test.ts b/nodejs/test/e2e/user_settings_edit.e2e.test.ts new file mode 100644 index 0000000000..857a1bfdcf --- /dev/null +++ b/nodejs/test/e2e/user_settings_edit.e2e.test.ts @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; +import { describe, expect, it } from "vitest"; +import { editUserSettings } from "../../examples/internal-user-settings.js"; +import { createSdkTestContext } from "./harness/sdkTestContext.js"; + +describe("Internal user settings edit contract", async () => { + const { copilotClient: client, workDir, env } = await createSdkTestContext(); + + it("runs the edit example and preserves settings-only reads and safe writes", async () => { + await client.start(); + // These host-only methods deliberately stay off the public SDK surface. + const settings = client["internalRpc"].user.settings; + const home = env.COPILOT_HOME; + writeFileSync(join(home, "config.json"), '{"voice":{"enabled":false}}'); + writeFileSync(join(home, "settings.json"), '{"voice":{"selectedModel":"local"}}'); + + expect(await settings.loadForEdit()).toEqual({ + status: "ok", + settings: { voice: { selectedModel: "local" } }, + }); + + const configDir = join(workDir, "example-config"); + mkdirSync(configDir); + const result = await editUserSettings(settings, configDir); + expect(result).toEqual({ value: { enabled: true, selectedModel: null } }); + expect(JSON.parse(readFileSync(join(configDir, "settings.json"), "utf8"))).toEqual({ + voice: result.value, + }); + console.log("Internal settings example:", JSON.stringify(result)); + + await settings.update({ + key: "voice", + value: { enabled: false }, + configDir, + subDir: "profile", + }); + expect(JSON.parse(readFileSync(join(configDir, "settings/profile.json"), "utf8"))).toEqual({ + voice: { enabled: false }, + }); + await expect( + settings.update({ + key: "voice", + value: { enabled: false }, + configDir, + subDir: "../../outside", + }) + ).rejects.toThrow("`subDir` must be a single path component"); + + const file = join(configDir, "settings.json"); + const original = "{ invalid settings"; + writeFileSync(file, original); + await expect( + settings.update({ key: "voice", value: { enabled: false }, configDir }) + ).rejects.toThrow("refusing to overwrite settings.json"); + expect(readFileSync(file, "utf8")).toBe(original); + expect(await settings.validate({ topKey: "voice", delete: true })).toEqual({ ok: true }); + }); +}); diff --git a/nodejs/test/e2e/websocket_fallback_recovery.e2e.test.ts b/nodejs/test/e2e/websocket_fallback_recovery.e2e.test.ts new file mode 100644 index 0000000000..671c7549bd --- /dev/null +++ b/nodejs/test/e2e/websocket_fallback_recovery.e2e.test.ts @@ -0,0 +1,277 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import http from "node:http"; +import { afterAll, describe, expect, it } from "vitest"; +import { WebSocketServer } from "ws"; +import { approveAll, defineTool, type SessionEvent } from "../../src/index.js"; +import { withTestCleanup } from "../helpers/withTestCleanup.js"; +import { createSdkTestContext, DEFAULT_GITHUB_TOKEN } from "./harness/sdkTestContext.js"; + +const FINAL_TEXT = "WS_FALLBACK_RECOVERY_DONE"; +const TOOL_NAME = "fallback_recovery_step"; + +type Transport = "ws" | "http"; +type ProviderRequest = { transport: Transport; connectionId?: number; body: ProviderBody }; +type ProviderBody = { + input?: Array<{ type?: string; call_id?: string; output?: unknown }>; + previous_response_id?: string; + stream?: boolean; +}; + +/** + * Scripts one agent loop: two tool rounds, then a final answer. The reply depends + * only on how many tool results the request carries, so either transport can + * serve any step and the assertions observe which transport the runtime chose. + */ +function responseEvents(body: ProviderBody, id: string): Array> { + const toolResults = (body.input ?? []).filter((item) => item.type === "function_call_output"); + const created = { + type: "response.created", + response: { id, object: "response", status: "in_progress", output: [] }, + }; + if (toolResults.length < 2) { + const call = { + type: "function_call", + id: `fc-${toolResults.length + 1}`, + call_id: `call-${toolResults.length + 1}`, + name: TOOL_NAME, + arguments: JSON.stringify({ step: toolResults.length + 1 }), + status: "completed", + }; + return [ + created, + { type: "response.output_item.added", output_index: 0, item: call }, + { type: "response.output_item.done", output_index: 0, item: call }, + { + type: "response.completed", + response: { id, object: "response", status: "completed", output: [call] }, + }, + ]; + } + const message = { + id: "msg-final", + type: "message", + role: "assistant", + content: [{ type: "output_text", text: FINAL_TEXT, annotations: [] }], + }; + return [ + created, + { + type: "response.output_item.added", + output_index: 0, + item: { ...message, content: [] }, + }, + { + type: "response.content_part.added", + output_index: 0, + content_index: 0, + part: { type: "output_text", text: "" }, + }, + { + type: "response.output_text.delta", + output_index: 0, + content_index: 0, + delta: FINAL_TEXT, + }, + { type: "response.output_text.done", output_index: 0, content_index: 0, text: FINAL_TEXT }, + { type: "response.output_item.done", output_index: 0, item: message }, + { + type: "response.completed", + response: { id, object: "response", status: "completed", output: [message] }, + }, + ]; +} + +/** An OpenAI-compatible provider whose first WebSocket request fails before any output. */ +async function startProvider() { + const requests: ProviderRequest[] = []; + const errors: unknown[] = []; + let connectionCount = 0; + let wsRequestCount = 0; + const server = http.createServer((req, res) => { + void (async () => { + if (req.method !== "POST" || req.url !== "/v1/responses") { + throw new Error(`Unexpected provider request: ${req.method} ${req.url}`); + } + req.setEncoding("utf8"); + let raw = ""; + for await (const chunk of req) raw += chunk; + const body = JSON.parse(raw) as ProviderBody; + if (body.stream !== true) { + // Session title generation is a one-shot request outside the agent loop. + res.writeHead(200, { "content-type": "application/json" }); + res.end( + JSON.stringify({ + id: "resp-title", + object: "response", + status: "completed", + output: [ + { + id: "msg-title", + type: "message", + role: "assistant", + content: [{ type: "output_text", text: "Title", annotations: [] }], + }, + ], + }) + ); + return; + } + requests.push({ transport: "http", body }); + res.writeHead(200, { "content-type": "text/event-stream" }); + for (const event of responseEvents(body, `resp-http-${requests.length}`)) { + res.write(`data: ${JSON.stringify(event)}\n\n`); + } + res.end(); + })().catch((error: unknown) => { + errors.push(error); + res.destroy(error instanceof Error ? error : new Error(String(error))); + }); + }); + const sockets = new WebSocketServer({ server, path: "/v1/responses" }); + sockets.on("connection", (socket) => { + const connectionId = ++connectionCount; + socket.on("error", (error) => errors.push(error)); + socket.on("message", (raw) => { + const body = JSON.parse(raw.toString()) as ProviderBody; + requests.push({ transport: "ws", connectionId, body }); + const events = + ++wsRequestCount === 1 + ? [ + { + type: "error", + error: { + code: "service_unavailable", + type: "websocket_error", + message: "Controlled transient WebSocket failure", + }, + }, + ] + : responseEvents(body, `resp-ws-${wsRequestCount}`); + for (const event of events) socket.send(JSON.stringify(event)); + }); + }); + await new Promise((resolve, reject) => { + server.once("error", reject); + server.listen(0, "127.0.0.1", resolve); + }); + const address = server.address(); + if (address === null || typeof address === "string") { + throw new Error("Expected a bound TCP address for the provider."); + } + return { + baseUrl: `http://127.0.0.1:${address.port}/v1`, + requests, + connectionCount: () => connectionCount, + async stop() { + for (const socket of sockets.clients) socket.terminate(); + await new Promise((resolve) => sockets.close(() => resolve())); + server.closeAllConnections(); + await new Promise((resolve, reject) => + server.close((error) => (error ? reject(error) : resolve())) + ); + if (errors.length > 0) throw new AggregateError(errors, "Provider fixture failed."); + }, + }; +} + +// The dispatcher's fallback state lives for one run (one user message's agent +// loop), so recovery is only observable across the model calls of one send. +describe.each([ + { recovery: true, transports: ["ws", "http", "http", "ws"] }, + { recovery: false, transports: ["ws", "http", "http", "http"] }, +])("WebSocket fallback recovery=$recovery", async ({ recovery, transports }) => { + const provider = await startProvider(); + // Register first so the SDK context stops its client before this provider. + afterAll(() => provider.stop()); + const flag = String(recovery); + const { copilotClient: client } = await createSdkTestContext({ + useStdio: true, + copilotClientOptions: { + gitHubToken: DEFAULT_GITHUB_TOKEN, + env: { + COPILOT_CLI_DISABLE_WEBSOCKET_RESPONSES: "false", + WEBSOCKET_FALLBACK_RECOVERY: flag, + COPILOT_EXP_COPILOT_CLI_WEBSOCKET_FALLBACK_RECOVERY: flag, + }, + }, + }); + + it(`${recovery ? "returns to" : "stays off"} the socket after a transient failure within one run`, async () => { + const toolCalls: number[] = []; + const session = await client.createSession({ + model: "gpt-5-responses", + streaming: true, + provider: { + type: "openai", + baseUrl: provider.baseUrl, + apiKey: "test-provider-key", + wireApi: "responses", + transport: "websockets", + }, + tools: [ + defineTool<{ step: number }>(TOOL_NAME, { + description: "Records one step of the controlled agent loop", + parameters: { + type: "object", + properties: { step: { type: "number" } }, + required: ["step"], + }, + defer: "never", + handler: ({ step }) => { + toolCalls.push(step); + return `STEP_${step}_RESULT`; + }, + }), + ], + onPermissionRequest: approveAll, + }); + const events: SessionEvent[] = []; + const unsubscribe = session.on((event) => events.push(event)); + await withTestCleanup( + async () => { + const answer = await session.sendAndWait({ prompt: "Run the two steps." }); + // Validate the final assistant response arrived (guards against truncated captures) + expect(answer?.data.content).toBe(FINAL_TEXT); + + const { requests } = provider; + expect(requests.map((request) => request.transport)).toEqual(transports); + // The failed socket request is replayed once over HTTP, then each + // tool runs exactly once whichever transport carried its turn. + expect(toolCalls).toEqual([1, 2]); + expect(events.filter((event) => event.type === "session.error")).toEqual([]); + const deltas = events.flatMap((event) => + event.type === "assistant.message_delta" ? [event.data.deltaContent] : [] + ); + expect(deltas.join("")).toBe(FINAL_TEXT); + + const last = requests.at(-1)!; + const results = (last.body.input ?? []).filter( + (item) => item.type === "function_call_output" + ); + expect(results.map((item) => item.call_id)).toEqual(["call-1", "call-2"]); + expect(JSON.stringify(results)).toContain("STEP_1_RESULT"); + expect(JSON.stringify(results)).toContain("STEP_2_RESULT"); + if (recovery) { + // A fresh socket carries the full history, not a continuation + // of the response it never produced. + expect(last.connectionId).toBe(2); + expect(last.body.previous_response_id).toBeUndefined(); + expect(provider.connectionCount()).toBe(2); + } else { + expect(provider.connectionCount()).toBe(1); + } + }, + unsubscribe, + () => session.disconnect(), + async () => { + const errors = await client.stop(); + if (errors.length > 0) { + throw new AggregateError(errors, "Client cleanup failed"); + } + } + ); + }); +}); diff --git a/nodejs/test/e2e/websocket_http_fallback.e2e.test.ts b/nodejs/test/e2e/websocket_http_fallback.e2e.test.ts new file mode 100644 index 0000000000..d8cd382285 --- /dev/null +++ b/nodejs/test/e2e/websocket_http_fallback.e2e.test.ts @@ -0,0 +1,143 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { afterAll, describe, expect, it } from "vitest"; +import { z } from "zod"; +import { approveAll, defineTool, type SessionEvent } from "../../src/index.js"; +import { withTestCleanup } from "../helpers/withTestCleanup.js"; +import { createSdkTestContext, DEFAULT_GITHUB_TOKEN } from "./harness/sdkTestContext.js"; +import { + createCompletedResponse, + startWebSocketResponsesServer, +} from "./harness/webSocketResponsesServer.js"; + +const TOOL_NAME = "ws_fallback_marker"; +const TOOL_RESULT = "WS_FALLBACK_TOOL_RESULT"; +const FINAL = "WS_FALLBACK_FINAL_ANSWER"; +const usage = { + input_tokens: 10, + output_tokens: 5, + total_tokens: 15, + input_tokens_details: { cached_tokens: 0 }, + output_tokens_details: { reasoning_tokens: 0 }, +}; + +describe("WebSocket to HTTP fallback telemetry", async () => { + const upstream = await startWebSocketResponsesServer(); + // Register first so the SDK context stops its client and proxy before this provider. + afterAll(() => upstream.stop()); + const { copilotClient: client, openAiEndpoint } = await createSdkTestContext({ + useStdio: true, + copilotClientOptions: { + gitHubToken: DEFAULT_GITHUB_TOKEN, + env: { + COPILOT_API_URL: upstream.baseUrl, + COPILOT_CLI_DISABLE_WEBSOCKET_RESPONSES: "false", + }, + }, + }); + upstream.forwardHttpTo(openAiEndpoint.url); + + it("describes the WebSocket failure on the downgrading call and keeps it sticky", async () => { + const { state } = upstream; + state.responses.push({ + type: "error", + error: { + code: "service_unavailable", + type: "websocket_error", + message: "controlled WebSocket outage", + }, + }); + state.httpResponses.push( + [ + { + type: "response.completed", + response: { + ...createCompletedResponse("", "resp-http-tool"), + output: [ + { + type: "function_call", + id: "fc-1", + call_id: "call-1", + name: TOOL_NAME, + arguments: JSON.stringify({ input: "ping" }), + status: "completed", + }, + ], + output_text: "", + usage, + }, + }, + ], + [ + { + type: "response.completed", + response: { ...createCompletedResponse(FINAL, "resp-http-final"), usage }, + }, + ] + ); + const session = await client.createSession({ + model: "gpt-5-responses", + onPermissionRequest: approveAll, + tools: [ + defineTool(TOOL_NAME, { + description: "Returns a fixed marker", + parameters: z.object({ input: z.string() }), + handler: () => TOOL_RESULT, + }), + ], + }); + const events: SessionEvent[] = []; + const unsubscribe = session.on((event) => events.push(event)); + await withTestCleanup( + async () => { + const answer = await session.sendAndWait({ prompt: "Use the marker tool." }); + // Validate the final assistant response arrived (guards against truncated captures) + expect(answer?.data.content).toBe(FINAL); + + expect(state.messages).toHaveLength(1); + expect(state.httpResponseRequestPaths).toHaveLength(2); + expect(state.httpResponses).toHaveLength(0); + expect( + events.some( + (event) => + event.type === "tool.execution_complete" && + JSON.stringify(event.data).includes(TOOL_RESULT) + ) + ).toBe(true); + + const usages = events.flatMap((event) => + event.type === "assistant.usage" ? [event.data] : [] + ); + expect(usages).toHaveLength(2); + const descriptors = { + websocketFallbackReason: "api_error", + websocketFallbackErrorKind: "http_status", + websocketFallbackStatusCode: 503, + websocketFallbackReconnectAttempted: false, + }; + expect(usages[0]).toMatchObject({ + ...descriptors, + websocketFallbackStartedThisCall: true, + }); + expect(usages[0].websocketFallbackCloseCode).toBeUndefined(); + expect(usages[1]).toMatchObject({ + ...descriptors, + websocketFallbackStartedThisCall: false, + }); + expect( + usages.every((data) => typeof data.websocketFallbackAfterMs === "number") + ).toBe(true); + }, + unsubscribe, + () => session.disconnect(), + async () => { + const errors = await client.stop(); + if (errors.length > 0) { + throw new AggregateError(errors, "WebSocket fallback client cleanup failed"); + } + } + ); + }); +}); diff --git a/nodejs/test/e2e/workflow.e2e.test.ts b/nodejs/test/e2e/workflow.e2e.test.ts index 349c7b7783..5d2673ea40 100644 --- a/nodejs/test/e2e/workflow.e2e.test.ts +++ b/nodejs/test/e2e/workflow.e2e.test.ts @@ -532,10 +532,15 @@ it("allows a module-level extension watcher to start a workflow while another bo const extensionDir = join(workDir, ".github", "extensions", "workflow-smoke"); await using session = await setupWorkflowExtension(workDir); - const parked = Promise.allSettled([ - session.workflow.run("parked", { notifyOnComplete: false }), - ]); + // Admission must finish before starting the body-entry deadline. + const parked = await session.rpc.workflow.run({ + name: "parked", + args: {}, + options: { notifyOnComplete: false }, + }); + const completion = Promise.allSettled([session.workflow.waitForRun(parked.runId)]); try { + expect(parked.status).toBe("running"); await retry( "wait for the parked workflow to enter its body", async () => { @@ -564,14 +569,14 @@ it("allows a module-level extension watcher to start a workflow while another bo ); } finally { writeFileSync(join(extensionDir, "release"), "release"); - const [outcome] = await parked; + const [outcome] = await completion; if (outcome.status === "rejected") { console.error("Parked workflow failed during cleanup:", outcome.reason); } else if (outcome.value.status !== "completed") { console.error("Parked workflow did not complete during cleanup:", outcome.value); } } - await expect(parked).resolves.toMatchObject([ + await expect(completion).resolves.toMatchObject([ { status: "fulfilled", value: { status: "completed", result: "released" }, diff --git a/nodejs/test/e2e/workiq_discovery.e2e.test.ts b/nodejs/test/e2e/workiq_discovery.e2e.test.ts index 249d982770..1d30226112 100644 --- a/nodejs/test/e2e/workiq_discovery.e2e.test.ts +++ b/nodejs/test/e2e/workiq_discovery.e2e.test.ts @@ -15,7 +15,7 @@ import { createE2eRequestHandler } from "../../../test/harness/mockHandlers.js"; describe("account-derived WorkIQ", async () => { const { env, workDir, openAiEndpoint } = await createSdkTestContext(); - it("discovers disabled WorkIQ without requesting authentication or persisting a server", async () => { + it("discovers WorkIQ only on supported platforms without requesting authentication or persisting a server", async () => { const profile = join(workDir, "workiq-profile"); await mkdir(profile, { recursive: true }); await writeFile( @@ -53,22 +53,29 @@ describe("account-derived WorkIQ", async () => { ]) ); const discovered = await session.rpc.mcp.list(); - expect(discovered.servers).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - name: "WorkIQ", - source: "account", - status: "disabled", - url: "https://workiq.svc.cloud.microsoft/mcp", - }), - ]) - ); + const expected = + process.platform === "linux" + ? [] + : [ + expect.objectContaining({ + name: "WorkIQ", + source: "account", + status: "disabled", + url: "https://workiq.svc.cloud.microsoft/mcp", + }), + ]; + expect(discovered.servers.filter((server) => server.name === "WorkIQ")).toEqual(expected); expect(discovered).not.toHaveProperty("accountServerConfigs"); - await session.rpc.mcp.reload(); + // Gating off WorkIQ can leave this fixture with no MCP configuration to reload. + if (discovered.servers.length === 0) { + await expect(session.rpc.mcp.reload()).rejects.toThrow( + "MCP config reload not available" + ); + } else { + await session.rpc.mcp.reload(); + } const reloaded = await session.rpc.mcp.list(); - expect(reloaded.servers.find((server) => server.name === "WorkIQ")?.status).toBe( - "disabled" - ); + expect(reloaded.servers.filter((server) => server.name === "WorkIQ")).toEqual(expected); expect(authenticationRequests).toBe(0); await expect(readFile(join(profile, "mcp-config.json"))).rejects.toMatchObject({ code: "ENOENT", @@ -142,7 +149,7 @@ describe("account-derived WorkIQ", async () => { expect(result).not.toHaveProperty("accountServerConfigs"); }); - it.each(["silent", "browser"] as const)( + it.skipIf(process.platform === "linux").each(["silent", "browser"] as const)( "authenticates through %s, persists renewal, and disconnects on parent logout", async (authentication) => { const profile = join(workDir, `workiq-${authentication}-profile`); diff --git a/nodejs/test/extension.test.ts b/nodejs/test/extension.test.ts index 9bb6b11ae9..132f5a8fc7 100644 --- a/nodejs/test/extension.test.ts +++ b/nodejs/test/extension.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import { CopilotClient } from "../src/client.js"; -import { approveAll } from "../src/index.js"; +import { approveAll, type CopilotSession } from "../src/index.js"; import { createCanvas, defineWorkflow, joinSession } from "../src/extension.js"; import { defaultJoinSessionPermissionHandler } from "../src/types.js"; @@ -111,6 +111,22 @@ describe("joinSession", () => { }); }); + it.each([true, false])( + "strips image generation consent (%s) from extension joins", + async (enabled) => { + process.env.SESSION_ID = "session-123"; + const resumeForExtension = vi + .spyOn(CopilotClient.prototype, "resumeSessionForExtension") + .mockResolvedValue({} as CopilotSession); + + const config = { tools: [], imageGeneration: { enabled } }; + await joinSession(config); + + const [, forwarded] = resumeForExtension.mock.calls[0]!; + expect(forwarded).not.toHaveProperty("imageGeneration"); + } + ); + it("exports the canvas helper from the extension surface", () => { const canvas = createCanvas({ id: "counter", diff --git a/nodejs/test/ffiRuntimeHost.test.ts b/nodejs/test/ffiRuntimeHost.test.ts index b5badfb27c..f7e19686fd 100644 --- a/nodejs/test/ffiRuntimeHost.test.ts +++ b/nodejs/test/ffiRuntimeHost.test.ts @@ -109,6 +109,18 @@ describe("FfiRuntimeHost callback cleanup", () => { vi.useRealTimers(); }); + it("forwards the CLI login sync integration without a CLI entrypoint", async () => { + const host = FfiRuntimeHost.create("runtime.node", undefined, undefined, [ + "--cli-login-sync-integration-id=github/github-app", + ]); + await host.start(); + + const argv = JSON.parse(ffi.hostStart.async.mock.calls[0][0].toString()) as string[]; + expect(argv).toEqual(["--cli-login-sync-integration-id=github/github-app"]); + + await host.dispose(); + }); + it("finishes disposal after a false close while retaining resources for the detached retry", async () => { ffi.connectionClose.async.mockImplementationOnce((_id, callback) => callback(null, false)); const host = FfiRuntimeHost.create("runtime.node", undefined, undefined, []); diff --git a/nodejs/test/image-generation.test.ts b/nodejs/test/image-generation.test.ts new file mode 100644 index 0000000000..93e5a4d71b --- /dev/null +++ b/nodejs/test/image-generation.test.ts @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +import { once } from "node:events"; +import { createServer } from "node:net"; +import { describe, expect, it, onTestFinished } from "vitest"; +import { + createMessageConnection, + StreamMessageReader, + StreamMessageWriter, + type MessageConnection, +} from "vscode-jsonrpc/node.js"; +import { + approveAll, + CopilotClient, + RuntimeConnection, + type ImageGenerationConfig, +} from "../src/index.js"; +import { getSdkProtocolVersion } from "../src/sdkProtocolVersion.js"; + +describe("image generation request serialization", () => { + it.each([undefined, {}, { enabled: true }, { enabled: false }] satisfies ( + | ImageGenerationConfig + | undefined + )[])("forwards %j on create and resume", async (imageGeneration) => { + const requests = new Map>(); + const connections: MessageConnection[] = []; + const server = createServer((socket) => { + const connection = createMessageConnection( + new StreamMessageReader(socket), + new StreamMessageWriter(socket) + ); + connections.push(connection); + connection.onRequest((method, params) => { + if (method === "connect") { + return { ok: true, protocolVersion: getSdkProtocolVersion(), version: "test" }; + } + if (method === "session.create" || method === "session.resume") { + const payload = params as Record; + requests.set(method, payload); + return { sessionId: payload.sessionId }; + } + return {}; + }); + connection.listen(); + }); + onTestFinished(async () => { + for (const connection of connections) connection.dispose(); + await new Promise((resolve, reject) => + server.close((error) => (error ? reject(error) : resolve())) + ); + }); + server.listen(0, "127.0.0.1"); + await once(server, "listening"); + const address = server.address(); + if (!address || typeof address === "string") throw new Error("Missing TCP address"); + const client = new CopilotClient({ + connection: RuntimeConnection.forUri(`127.0.0.1:${address.port}`), + }); + onTestFinished(() => client.forceStop()); + const session = await client.createSession({ + onPermissionRequest: approveAll, + imageGeneration, + }); + await client.resumeSession(session.sessionId, { + onPermissionRequest: approveAll, + imageGeneration, + }); + for (const method of ["session.create", "session.resume"]) { + const payload = requests.get(method); + expect(payload).toBeDefined(); + if (imageGeneration === undefined) { + expect(payload).not.toHaveProperty("imageGeneration"); + } else { + expect(payload?.imageGeneration).toEqual(imageGeneration); + } + } + }); +}); diff --git a/nodejs/test/java-codegen.test.ts b/nodejs/test/java-codegen.test.ts index d44fb039a5..28db7fe54a 100644 --- a/nodejs/test/java-codegen.test.ts +++ b/nodejs/test/java-codegen.test.ts @@ -1,6 +1,11 @@ +import type { JSONSchema7 } from "json-schema"; import { describe, expect, it } from "vitest"; -import { renderRpcTypes, renderRpcWrappers } from "../../java/scripts/codegen/java.ts"; +import { + generateRpcClass, + renderRpcTypes, + renderRpcWrappers, +} from "../../java/scripts/codegen/java.ts"; function rpcSource(files: Map, name: string): string { const source = files.get( @@ -10,6 +15,126 @@ function rpcSource(files: Map, name: string): string { return source!; } +describe("java RPC primitive results", () => { + it.each([ + ["boolean", "Boolean"], + ["integer", "Long"], + ["number", "Double"], + ["string", "String"], + ] as const)("preserves inline and referenced %s results", async (type, javaType) => { + const schema = { + server: { + sample: { + inline: { + rpcMethod: "sample.inline", + params: null, + result: { type }, + }, + referenced: { + rpcMethod: "sample.referenced", + visibility: "internal", + params: null, + result: { $ref: "#/definitions/ScalarResult" }, + }, + empty: { rpcMethod: "sample.empty", params: null, result: null }, + }, + }, + definitions: { ScalarResult: { type } }, + } satisfies Parameters[0]; + const code = rpcSource(await renderRpcWrappers(schema), "ServerSampleApi"); + + expect(code).toContain(`CompletableFuture<${javaType}> inline()`); + expect(code).toContain(`"sample.inline", java.util.Map.of(), ${javaType}.class`); + expect(code).toContain(`CompletableFuture<${javaType}> referenced()`); + expect(code).toContain(`"sample.referenced", java.util.Map.of(), ${javaType}.class`); + expect(code).toContain("CompletableFuture empty()"); + expect(code).toContain('"sample.empty", java.util.Map.of(), Void.class'); + }); +}); + +it("preserves boolean-map RPC results without widening them to JsonNode", async () => { + const files = await renderRpcWrappers({ + server: { + sample: { + flags: { + rpcMethod: "sample.flags", + params: null, + result: { type: "object", additionalProperties: { type: "boolean" } }, + }, + document: { + rpcMethod: "sample.document", + params: null, + result: { type: "object", additionalProperties: true }, + }, + }, + }, + }); + const source = rpcSource(files, "ServerSampleApi"); + expect(source).toContain("import java.util.Map;"); + expect(source).toContain("CompletableFuture> flags()"); + expect(source).toContain("constructMapType(Map.class, String.class, Boolean.class)"); + expect(source).toContain("CompletableFuture document()"); +}); + +describe("Java required opaque JSON parameters", () => { + it.each([ + ["user", "applyTopKey"], + ["repo", "writeKey"], + ])("preserves optional value presence in %s.settings.%s", async (namespace, action) => { + const rpcMethod = `${namespace}.settings.${action}`; + const files = await renderRpcWrappers({ + server: { + [namespace]: { + settings: { + [action]: { + rpcMethod, + params: { + type: "object", + properties: { + value: { "x-opaque-json": true } as JSONSchema7, + delete: { type: "boolean" }, + }, + }, + result: null, + }, + }, + }, + }, + }); + const source = rpcSource( + files, + `Server${namespace === "user" ? "User" : "Repo"}SettingsApi` + ); + expect(source).toContain(`return caller.invoke("${rpcMethod}", params, Void.class);`); + expect(source).not.toContain('putNull("value")'); + }); + + it("preserves a required JSON null without emitting absent optional fields", () => { + const opaque = { "x-opaque-json": true } as JSONSchema7; + const { code } = generateRpcClass( + "SettingsUpdateParams", + { + type: "object", + required: ["value"], + properties: { + value: opaque, + optionalValue: opaque, + }, + }, + new Map(), + "com.github.copilot.generated.rpc", + "internal", + true + ); + expect(code).toContain( + '@JsonInclude(JsonInclude.Include.ALWAYS)\n @JsonProperty("value") Object value' + ); + expect(code).not.toContain( + '@JsonInclude(JsonInclude.Include.ALWAYS)\n @JsonProperty("optionalValue")' + ); + }); +}); + describe("java RPC codegen visibility", () => { const schema = { server: { diff --git a/nodejs/test/python-codegen.test.ts b/nodejs/test/python-codegen.test.ts index e87cefb5c9..36cb7e3133 100644 --- a/nodejs/test/python-codegen.test.ts +++ b/nodejs/test/python-codegen.test.ts @@ -67,6 +67,19 @@ describe("Python RPC projection compatibility", () => { expect(code).toContain(`return list(await self._client.request("${method}"`); }); + it("keeps the managed permission discriminator independent of MCP URL kinds", () => { + expect(code).toContain("class ManagedPermissionOperationKind(Enum):"); + const operation = code + .split("class ManagedPermissionOperation:")[1] + ?.split("\n\n@dataclass")[0]; + expect(operation).toContain("kind: ManagedPermissionOperationKind"); + expect(operation).toContain('kind = ManagedPermissionOperationKind(obj.get("kind"))'); + expect(operation).toContain( + 'result["kind"] = to_enum(ManagedPermissionOperationKind, self.kind)' + ); + expect(operation).not.toContain("MCPServerCardURLKind"); + }); + it("preserves the existing Workflow checkpoint result API", () => { const result = "SessionWorkflowPauseAtCheckpointResult"; expect(code).toContain(`class ${result}:`); diff --git a/nodejs/test/reasoning-summaries-types.test.ts b/nodejs/test/reasoning-summaries-types.test.ts new file mode 100644 index 0000000000..556b6765b4 --- /dev/null +++ b/nodejs/test/reasoning-summaries-types.test.ts @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { expectTypeOf, it } from "vitest"; +import type { + CopilotSession, + ModelGetReasoningSummariesRequest, + ModelGetReasoningSummariesResult, +} from "../src/index.js"; + +it("exports reasoning-summary request and result types from the package root", () => { + expectTypeOf().toEqualTypeOf< + Parameters[0] + >(); + expectTypeOf().toEqualTypeOf< + Awaited> + >(); +}); diff --git a/nodejs/test/rust-codegen.test.ts b/nodejs/test/rust-codegen.test.ts index 5f34d804b1..ce3ee6b426 100644 --- a/nodejs/test/rust-codegen.test.ts +++ b/nodejs/test/rust-codegen.test.ts @@ -17,6 +17,45 @@ import { import { legacyRequestSchema } from "./legacy-parameters-fixture.ts"; describe("Rust API type codegen", () => { + it("reuses the named managed operation and kind in request and result fields", () => { + const code = generateApiTypesCode({ + definitions: { + ManagedPermissionOperation: { + anyOf: [ + { + type: "object", + properties: { + kind: { type: "string", const: "url" }, + url: { type: "string" }, + }, + required: ["kind", "url"], + }, + ], + }, + ManagedPermissionsEvaluateRequest: { + type: "object", + properties: { + operations: { + type: "array", + items: { $ref: "#/definitions/ManagedPermissionOperation" }, + }, + }, + required: ["operations"], + }, + ManagedPermissionEvaluation: { + type: "object", + properties: { operation: { $ref: "#/definitions/ManagedPermissionOperation" } }, + required: ["operation"], + }, + }, + } as ApiSchema); + expect(code).toContain("pub operations: Vec,"); + expect(code).toContain("pub operation: ManagedPermissionOperation,"); + expect(code).toContain("pub kind: ManagedPermissionOperationKind,"); + expect(code).not.toContain("ManagedPermissionsEvaluateRequestOperationsItem"); + expect(code).not.toContain("ManagedPermissionEvaluationOperation"); + }); + it("keeps every noncanonical open identifier distinct from the default sentinel", () => { const code = generateApiTypesCode( postProcessSchema({ @@ -782,6 +821,42 @@ pub enum ${name} { }); describe("Rust x-legacy-parameters", () => { + it.each(["server", "session"] as const)( + "serializes unrelated %s requests and options by value", + (scope) => { + const { schema } = legacyRequestSchema(1, scope); + const rpc = generateRpcCode(schema); + expect(rpc.match(/serde_json::to_value\(params\)\?/g)).toHaveLength(2); + expect(rpc).not.toContain("serde_json::to_value(¶ms)"); + } + ); + + it("preserves optional settings value presence without inserting null", () => { + const rpc = generateRpcCode({ + server: { + user: { + settings: { + applyTopKey: { + rpcMethod: "user.settings.applyTopKey", + params: { + type: "object", + properties: { + topKey: { type: "string" }, + value: { "x-opaque-json": true } as JSONSchema7, + delete: { type: "boolean" }, + }, + required: ["topKey"], + }, + result: { type: "object" }, + }, + }, + }, + }, + }); + expect(rpc).toContain("let wire_params = serde_json::to_value(params)?;"); + expect(rpc).not.toContain('_obj.insert("value"'); + }); + const render = (additions: 0 | 1 | 2, scope: "server" | "session" = "server") => { const { schema } = legacyRequestSchema(additions, scope); const types = generateApiTypesCode(schema); diff --git a/nodejs/test/toolSet.test.ts b/nodejs/test/toolSet.test.ts index b77b797072..eba35de989 100644 --- a/nodejs/test/toolSet.test.ts +++ b/nodejs/test/toolSet.test.ts @@ -498,6 +498,27 @@ describe("Empty-mode safe defaults", () => { expect(payload.enableSkills).toBe(true); }); + it("passes the create-time session name through on create", async () => { + const { client, spy } = await setupClient(); + await client.createSession({ + onPermissionRequest: approveAll, + availableTools: new ToolSet().addBuiltIn(BuiltInTools.Isolated), + name: "named at create", + }); + const payload = createPayload(spy); + expect(payload.name).toBe("named at create"); + }); + + it("omits the session name on create when it is not set", async () => { + const { client, spy } = await setupClient(); + await client.createSession({ + onPermissionRequest: approveAll, + availableTools: new ToolSet().addBuiltIn(BuiltInTools.Isolated), + }); + const payload = createPayload(spy); + expect(payload.name).toBeUndefined(); + }); + it("passes organizationCustomInstructions through on create", async () => { const { client, spy } = await setupClient(); await client.createSession({ diff --git a/nodejs/tsconfig.test.json b/nodejs/tsconfig.test.json index 6f8ef72510..f6f04f3548 100644 --- a/nodejs/tsconfig.test.json +++ b/nodejs/tsconfig.test.json @@ -13,6 +13,7 @@ "test/ffiRuntimeHost.test.ts", "test/session-event-types.test.ts", "test/session-config-types.test.ts", + "test/reasoning-summaries-types.test.ts", "test/sandbox-config.test.ts", "test/message-source.test.ts", "test/legacy-request-compatibility.test.ts" diff --git a/nodejs/vitest.config.ts b/nodejs/vitest.config.ts index 54c1d1681b..d9da632555 100644 --- a/nodejs/vitest.config.ts +++ b/nodejs/vitest.config.ts @@ -52,6 +52,7 @@ const inProcessBlockedE2E = [ "**/test/e2e/system_message_transform.e2e.test.ts", "**/test/e2e/tool_results.e2e.test.ts", "**/test/e2e/tools.e2e.test.ts", + "**/test/e2e/tools_anthropic_updates.e2e.test.ts", ]; // These suites own their provider or CAPI auth/request-handler setup. They run @@ -69,6 +70,8 @@ const selfConfiguredBackendE2E = [ "**/test/e2e/per_session_auth.e2e.test.ts", "**/test/e2e/provider_endpoint.e2e.test.ts", "**/test/e2e/subagent_prompt_resume.e2e.test.ts", + "**/test/e2e/tools_anthropic_updates.e2e.test.ts", + "**/test/e2e/websocket_http_fallback.e2e.test.ts", "**/test/e2e/websocket_terminals.e2e.test.ts", ]; diff --git a/python/copilot/__init__.py b/python/copilot/__init__.py index 9e9a9fa8bf..ae3aa7e7ac 100644 --- a/python/copilot/__init__.py +++ b/python/copilot/__init__.py @@ -157,6 +157,7 @@ ExitPlanModeRequest, ExitPlanModeResult, GitHubMcpToolConfig, + ImageGenerationConfig, InfiniteSessionConfig, InputOptions, LargeToolOutputConfig, @@ -464,6 +465,7 @@ "ToolResult", "ToolResultType", "ToolSearchConfig", + "ImageGenerationConfig", "ToolSet", "UriRuntimeConnection", "UserInputHandler", diff --git a/python/copilot/client.py b/python/copilot/client.py index 2d0230023c..b1f7947e0c 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -114,6 +114,7 @@ ElicitationHandler, ExitPlanModeHandler, GitHubMcpToolConfig, + ImageGenerationConfig, InfiniteSessionConfig, LargeToolOutputConfig, McpAuthHandler, @@ -1751,6 +1752,9 @@ def __init__( self._ffi_host: FfiRuntimeHost | None = None self._inprocess_runtime_path: str | None = None self._inprocess_cli_entrypoint: str | None = None + self._cli_login_sync_integration_id = ( + os.environ.get("GITHUB_COPILOT_INTEGRATION_ID", "").strip() or None + ) if isinstance(connection, UriRuntimeConnection): if connection.connection_token is not None and len(connection.connection_token) == 0: @@ -2351,6 +2355,7 @@ async def create_session( on_permission_request: _PermissionHandlerFn | None = None, model: str | None = None, session_id: str | None = None, + name: str | None = None, client_name: str | None = None, reasoning_effort: ReasoningEffort | None = None, reasoning_summary: ReasoningSummary | None = None, @@ -2359,6 +2364,7 @@ async def create_session( tools: list[Tool] | None = None, system_message: SystemMessageConfig | None = None, tool_search: ToolSearchConfig | None = None, + image_generation: ImageGenerationConfig | None = None, available_tools: list[str] | ToolSet | None = None, excluded_tools: list[str] | ToolSet | None = None, on_user_input_request: UserInputHandler | None = None, @@ -2403,6 +2409,8 @@ async def create_session( included_builtin_skills: list[str] | None = None, skill_directories: list[str] | None = None, skill_provider: SkillProvider | None = None, + agent_directories: list[str] | None = None, + hook_directories: list[str] | None = None, plugin_directories: list[str] | None = None, instruction_directories: list[str] | None = None, disabled_skills: list[str] | None = None, @@ -2449,6 +2457,12 @@ async def create_session( for the consumer to resolve via the pending permission RPC. model: The model to use for the session (e.g. ``"gpt-4"``). session_id: Optional session ID. If not provided, a UUID is generated. + name: Names the session at creation, as ``session.name.set`` would, and + is validated the same way: non-empty, no leading or trailing whitespace, + at most 100 UTF-16 units, no control characters, and no double quotes. + A name also makes the session persist from the start, so it can be listed + and resumed without ever running a turn. Local sessions only: cloud + creation rejects it. client_name: Optional client name for identification. reasoning_effort: Reasoning effort level for the model. reasoning_summary: Reasoning summary mode for supported models. @@ -2549,18 +2563,32 @@ async def create_session( organization_custom_instructions: Organization-level custom instructions. enable_on_demand_instruction_discovery: Enables on-demand instruction file discovery. - enable_file_hooks: Enables file-based hooks from ``.github/hooks/``. + enable_file_hooks: Enables repository-origin file hooks from ``.github/hooks/`` + and trusted repository-configured roots, not explicit hook_directories + or callback hooks. enable_host_git_operations: Enables git operations on the host filesystem. enable_session_store: Enables the cross-session store. enable_skills: Enables skill loading. - skill_directories: Directories to search for skills. + skill_directories: Explicit discovery-only skill roots. No general filesystem + grant. Available without enable_config_discovery, subject to skill/managed + disables. Relative paths resolve once when supplied against the request's + effective working_directory; retained roots do not move on cwd changes. + None and omission are equivalent; [] clears only the explicit SDK roots. skill_provider: **Experimental.** Session-scoped provider for SDK-owned skills. Providers are not persisted; re-supply the provider when resuming the session. Synchronous providers run on the event loop, so keep them fast or make them async. Not supported for cloud sessions. - instruction_directories: Additional directories to search for custom - instruction files. + agent_directories: Explicit discovery-only agent roots, separate from + custom_agents. Same path and clearing rules as skill_directories. Available + without enable_config_discovery, subject to source/managed disables. + hook_directories: Explicit discovery-only file-hook roots, separate from callback + hooks. Same path and clearing rules as skill_directories. Available without + enable_config_discovery or enable_file_hooks (repository-origin hooks only); + hook and managed disables still apply. + instruction_directories: Explicit discovery-only instruction roots using existing + SDK formats. Same path and clearing rules as skill_directories. Available + without enable_config_discovery, subject to instruction/managed disables. disabled_skills: Skills to disable. disabled_mcp_servers: Exact MCP server names to disable only for this session. Disabled servers are not started or authenticated on @@ -2568,6 +2596,8 @@ async def create_session( already running. This does not change global MCP settings. infinite_sessions: Infinite session configuration. memory: Session memory configuration. + image_generation: **Experimental.** Image generation opt-in. Not persisted; re-supply + after runtime restart. See :class:`ImageGenerationConfig`. cloud: Creates a remote session in the cloud instead of a local session. Optionally associates repository metadata with the cloud session. @@ -2715,6 +2745,8 @@ async def create_session( if tool_search is not None: payload["toolSearch"] = _tool_search_to_wire(tool_search) + if image_generation is not None: + payload["imageGeneration"] = dict(image_generation) if available_tools is not None: payload["availableTools"] = available_tools @@ -2891,8 +2923,12 @@ async def create_session( payload["hasSkillProvider"] = True # Add skill directories configuration if provided - if skill_directories: + if skill_directories is not None: payload["skillDirectories"] = skill_directories + if agent_directories is not None: + payload["agentDirectories"] = agent_directories + if hook_directories is not None: + payload["hookDirectories"] = hook_directories # Add plugin directories configuration if provided if plugin_directories: @@ -2960,6 +2996,8 @@ async def create_session( ) if local_session_id is not None: payload["sessionId"] = local_session_id + if name is not None: + payload["name"] = name github_token_provider_registration_id = self._register_github_token_provider( github_token_provider, local_session_id ) @@ -3239,6 +3277,7 @@ async def resume_session( tools: list[Tool] | None = None, system_message: SystemMessageConfig | None = None, tool_search: ToolSearchConfig | None = None, + image_generation: ImageGenerationConfig | None = None, available_tools: list[str] | ToolSet | None = None, excluded_tools: list[str] | ToolSet | None = None, on_user_input_request: UserInputHandler | None = None, @@ -3282,6 +3321,8 @@ async def resume_session( included_builtin_skills: list[str] | None = None, skill_directories: list[str] | None = None, skill_provider: SkillProvider | None = None, + agent_directories: list[str] | None = None, + hook_directories: list[str] | None = None, plugin_directories: list[str] | None = None, instruction_directories: list[str] | None = None, disabled_skills: list[str] | None = None, @@ -3426,17 +3467,31 @@ async def resume_session( organization_custom_instructions: Organization-level custom instructions. enable_on_demand_instruction_discovery: Enables on-demand instruction file discovery. - enable_file_hooks: Enables file-based hooks from ``.github/hooks/``. + enable_file_hooks: Enables repository-origin file hooks from ``.github/hooks/`` + and trusted repository-configured roots, not explicit hook_directories + or callback hooks. enable_host_git_operations: Enables git operations on the host filesystem. enable_session_store: Enables the cross-session store. enable_skills: Enables skill loading. - skill_directories: Directories to search for skills. + skill_directories: Explicit discovery-only skill roots. No general filesystem + grant. Available without enable_config_discovery, subject to skill/managed + disables. Relative paths resolve once when supplied against the request's + effective working_directory; retained roots do not move on cwd changes. + None and omission are equivalent; [] clears only the explicit SDK roots. skill_provider: **Experimental.** Session-scoped provider for SDK-owned skills. Providers are not persisted; callers must re-supply the provider on resume. Synchronous providers run on the event loop, so keep them fast or make them async. - instruction_directories: Additional directories to search for custom - instruction files. + agent_directories: Explicit discovery-only agent roots, separate from + custom_agents. Same path and clearing rules as skill_directories. Available + without enable_config_discovery, subject to source/managed disables. + hook_directories: Explicit discovery-only file-hook roots, separate from callback + hooks. Same path and clearing rules as skill_directories. Available without + enable_config_discovery or enable_file_hooks (repository-origin hooks only); + hook and managed disables still apply. + instruction_directories: Explicit discovery-only instruction roots using existing + SDK formats. Same path and clearing rules as skill_directories. Available + without enable_config_discovery, subject to instruction/managed disables. disabled_skills: Skills to disable. disabled_mcp_servers: Exact MCP server names to disable only for this session. Disabled servers are not started or authenticated on @@ -3444,6 +3499,8 @@ async def resume_session( already running. This does not change global MCP settings. infinite_sessions: Infinite session configuration. memory: Session memory configuration. + image_generation: **Experimental.** Image generation opt-in. Omission preserves a + resident session's setting; re-supply after runtime restart. on_event: Callback for session events. enable_mcp_apps: **Experimental.** Opt into MCP Apps (SEP-1865) UI passthrough on resume. This parameter is part of an experimental @@ -3589,6 +3646,8 @@ async def resume_session( payload["systemMessage"] = wire_system_message if tool_search is not None: payload["toolSearch"] = _tool_search_to_wire(tool_search) + if image_generation is not None: + payload["imageGeneration"] = dict(image_generation) if available_tools is not None: payload["availableTools"] = available_tools if excluded_tools is not None: @@ -3743,8 +3802,12 @@ async def resume_session( if agent: payload["agent"] = agent - if skill_directories: + if skill_directories is not None: payload["skillDirectories"] = skill_directories + if agent_directories is not None: + payload["agentDirectories"] = agent_directories + if hook_directories is not None: + payload["hookDirectories"] = hook_directories if plugin_directories: payload["pluginDirectories"] = plugin_directories if instruction_directories is not None: @@ -4549,6 +4612,8 @@ def _convert_custom_agent_to_wire_format( The custom agent configuration in camelCase wire format. """ wire_agent: dict[str, Any] = {"name": agent.get("name"), "prompt": agent.get("prompt")} + if "hooks" in agent: + wire_agent["hooks"] = agent["hooks"] if "display_name" in agent: wire_agent["displayName"] = agent["display_name"] if "description" in agent: @@ -4800,6 +4865,8 @@ async def _start_inprocess_ffi(self) -> None: args.extend(["--session-idle-timeout", str(opts.session_idle_timeout_seconds)]) if opts.enable_remote_sessions: args.append("--remote") + if self._cli_login_sync_integration_id is not None: + args.append(f"--cli-login-sync-integration-id={self._cli_login_sync_integration_id}") environment: dict[str, str] = {} if opts.github_token: diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index c4010c7315..e08198249b 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -6,7 +6,7 @@ from typing import ClassVar, TYPE_CHECKING -from .session_events import AbortReason, AgentModelPolicy, Attachment, AutoTier, ContextTier, EmbeddedBlobResourceContents, EmbeddedTextResourceContents, IndexedSearchState, ManagedSettingsResolvedSource, McpOauthHttpResponse, McpOauthWWWAuthenticateParams, McpServerMetadata, McpServerSource, McpServerStatus, ModelChangeSource, ModelProviderKind, ModelProviderRef, PermissionDecisionSource, PermissionMode, PermissionPromptRequest, PermissionRule, ProviderQuotaState, ReasoningSummary, RemediationAction, SessionEvent, SessionLimitsConfig, SessionMode, ShutdownType, SkillSource, TaskBlocker, TaskCompletionOutcome, UsageGetMetricsResult, UserToolSessionApproval, Verbosity, _load_Attachment, _load_PermissionPromptRequest, _load_UserToolSessionApproval +from .session_events import AbortReason, AgentModelPolicy, Attachment, AutoTier, ContextTier, EmbeddedBlobResourceContents, EmbeddedTextResourceContents, IndexedSearchState, ManagedPermissionsContext, ManagedSettingsResolvedSource, McpOauthHttpResponse, McpOauthWWWAuthenticateParams, McpServerMetadata, McpServerSource, McpServerStatus, ModelChangeSource, ModelProviderKind, ModelProviderRef, PermissionDecisionSource, PermissionMode, PermissionPromptRequest, PermissionRule, ProviderQuotaState, ReasoningSummary, RemediationAction, SessionEvent, SessionLimitsConfig, SessionMode, ShutdownType, SkillSource, TaskBlocker, TaskCompletionOutcome, UsageGetMetricsResult, UserToolSessionApproval, Verbosity, _load_Attachment, _load_PermissionPromptRequest, _load_UserToolSessionApproval if TYPE_CHECKING: from .._jsonrpc import JsonRpcClient @@ -22,6 +22,7 @@ T = TypeVar("T") EnumT = TypeVar("EnumT", bound=Enum) +UNSET_SENTINEL = object() def from_str(x: Any) -> str: @@ -351,6 +352,31 @@ def to_dict(self) -> dict: class AccountAuthInfoType(Enum): ACCOUNT = "account" +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _AccountDescribeAuthInfoResult: + """The wording the runtime uses for an authentication identity.""" + + description: str + """One-line description of the identity, suitable after a verb such as "Switched to".""" + + message: str + """Sign-in banner announcing the authenticated user.""" + + @staticmethod + def from_dict(obj: Any) -> '_AccountDescribeAuthInfoResult': + assert isinstance(obj, dict) + description = from_str(obj.get("description")) + message = from_str(obj.get("message")) + return _AccountDescribeAuthInfoResult(description, message) + + def to_dict(self) -> dict: + result: dict = {} + result["description"] = from_str(self.description) + result["message"] = from_str(self.message) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class AccountGetCurrentAuthResult: @@ -377,6 +403,38 @@ def to_dict(self) -> dict: result["authInfo"] = from_union([lambda x: (x).to_dict(), from_none], self.auth_info) return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _AccountGetGistSupportResult: + """Whether the identity can create gists, and why not when it cannot.""" + + supported: bool + """True when the identity can create gists.""" + + kind: str | None = None + """Machine-readable reason category when unsupported.""" + + reason: str | None = None + """Human-readable explanation to show where the gist action would have been.""" + + @staticmethod + def from_dict(obj: Any) -> '_AccountGetGistSupportResult': + assert isinstance(obj, dict) + supported = from_bool(obj.get("supported")) + kind = from_union([from_str, from_none], obj.get("kind")) + reason = from_union([from_str, from_none], obj.get("reason")) + return _AccountGetGistSupportResult(supported, kind, reason) + + def to_dict(self) -> dict: + result: dict = {} + result["supported"] = from_bool(self.supported) + if self.kind is not None: + result["kind"] = from_union([from_str, from_none], self.kind) + if self.reason is not None: + result["reason"] = from_union([from_str, from_none], self.reason) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class AccountGetQuotaRequest: @@ -460,6 +518,49 @@ def to_dict(self) -> dict: result["resetDate"] = from_union([from_str, from_none], self.reset_date) return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _AccountGetTokenEnvVarResult: + """The environment variable supplying a classic personal access token, when one is set.""" + + name: str | None = None + """Variable name, for example `GH_TOKEN`. Absent when no classic token is present in the + environment. + """ + + @staticmethod + def from_dict(obj: Any) -> '_AccountGetTokenEnvVarResult': + assert isinstance(obj, dict) + name = from_union([from_str, from_none], obj.get("name")) + return _AccountGetTokenEnvVarResult(name) + + def to_dict(self) -> dict: + result: dict = {} + if self.name is not None: + result["name"] = from_union([from_str, from_none], self.name) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _AccountGetWebURLResult: + """An absolute URL on the identity's GitHub host.""" + + url: str + """Absolute URL for the requested path.""" + + @staticmethod + def from_dict(obj: Any) -> '_AccountGetWebURLResult': + assert isinstance(obj, dict) + url = from_str(obj.get("url")) + return _AccountGetWebURLResult(url) + + def to_dict(self) -> dict: + result: dict = {} + result["url"] = from_str(self.url) + return result + # Experimental: this type is part of an experimental API and may change or be removed. class AccountKind(Enum): """The provider kind stamped on a signed-in account. @@ -808,6 +909,12 @@ def to_dict(self) -> dict: class AgentsDiscoverRequest: """Optional project paths to include in agent discovery.""" + agent_directories: list[str] | None = field(default=None, kw_only=True) + """Explicit discovery-only agent roots, separate from customAgents and filesystem grants. + Available without ambient discovery, subject to managed/source disables. Relative paths + resolve against the request's effective working directory when supplied. Null and + omission are equivalent; an empty array contributes no explicit roots. + """ exclude_host_agents: bool | None = None """When true, omit the host's agents (the user-level agent directory and all plugin agents), leaving only project and remote agents. For multitenant deployments. @@ -820,12 +927,15 @@ class AgentsDiscoverRequest: @staticmethod def from_dict(obj: Any) -> 'AgentsDiscoverRequest': assert isinstance(obj, dict) + agent_directories = from_union([lambda x: from_list(from_str, x), from_none], obj.get("agentDirectories")) exclude_host_agents = from_union([from_bool, from_none], obj.get("excludeHostAgents")) project_paths = from_union([lambda x: from_list(from_str, x), from_none], obj.get("projectPaths")) - return AgentsDiscoverRequest(exclude_host_agents, project_paths) + return AgentsDiscoverRequest(exclude_host_agents, project_paths, agent_directories=agent_directories) def to_dict(self) -> dict: result: dict = {} + if self.agent_directories is not None: + result["agentDirectories"] = from_union([lambda x: from_list(from_str, x), from_none], self.agent_directories) if self.exclude_host_agents is not None: result["excludeHostAgents"] = from_union([from_bool, from_none], self.exclude_host_agents) if self.project_paths is not None: @@ -1026,6 +1136,12 @@ def to_dict(self) -> dict: class AgentsGetDiscoveryPathsRequest: """Optional project paths to include when enumerating agent discovery directories.""" + agent_directories: list[str] | None = field(default=None, kw_only=True) + """Explicit discovery-only agent roots to enumerate, including configured missing + directories. Relative paths resolve against the request's effective working directory + when supplied. These are not filesystem grants or preferred creation targets. Null and + omission are equivalent. + """ exclude_host_agents: bool | None = None """When true, omit the host's user-level agent directory, leaving only project directories. For multitenant deployments (mirrors `discover`'s `excludeHostAgents`). @@ -1038,12 +1154,15 @@ class AgentsGetDiscoveryPathsRequest: @staticmethod def from_dict(obj: Any) -> 'AgentsGetDiscoveryPathsRequest': assert isinstance(obj, dict) + agent_directories = from_union([lambda x: from_list(from_str, x), from_none], obj.get("agentDirectories")) exclude_host_agents = from_union([from_bool, from_none], obj.get("excludeHostAgents")) project_paths = from_union([lambda x: from_list(from_str, x), from_none], obj.get("projectPaths")) - return AgentsGetDiscoveryPathsRequest(exclude_host_agents, project_paths) + return AgentsGetDiscoveryPathsRequest(exclude_host_agents, project_paths, agent_directories=agent_directories) def to_dict(self) -> dict: result: dict = {} + if self.agent_directories is not None: + result["agentDirectories"] = from_union([lambda x: from_list(from_str, x), from_none], self.agent_directories) if self.exclude_host_agents is not None: result["excludeHostAgents"] = from_union([from_bool, from_none], self.exclude_host_agents) if self.project_paths is not None: @@ -4142,6 +4261,180 @@ class ExternalToolTextResultForLlmContentTerminalType(Enum): class KindEnum(Enum): TEXT = "text" +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +class _FeatureFlagAvailability(Enum): + """The population a feature flag is declared for, before overrides. + + The population the flag is declared for before any environment or user-settings override + is applied. + """ + EXPERIMENTAL = "experimental" + OFF = "off" + ON = "on" + STAFF = "staff" + STAFF_AND_EXPERIMENTAL = "staff-and-experimental" + STAFF_GITHUB = "staff-github" + STAFF_MICROSOFT = "staff-microsoft" + STAFF_OR_EXPERIMENTAL = "staff-or-experimental" + TEAM = "team" + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _FeatureFlagsResolveRequest: + """Caller population and overrides for static feature-flag resolution, independent of + experiment assignments and active runtime state. + """ + is_experimental: bool + """Whether the caller is in an experimental population.""" + + is_staff: bool + """Whether the caller is GitHub or Microsoft staff.""" + + is_team: bool + """Whether the caller belongs to the team population used by feature availability.""" + + base_flags: dict[str, bool] | None = None + """Resolve against this already-computed flag set rather than each flag's declared + availability. Keyed by flag name. + """ + config: Any = None + """User settings to apply, including `enabledFeatureFlags`, legacy `feature_flags.enabled`, + and supported convenience settings. + """ + environment: dict[str, str] | None = None + """Environment for direct feature-flag overrides and `COPILOT_CLI_ENABLED_FEATURE_FLAGS`. + Does not apply `COPILOT_EXP_*` experiment assignments. Defaults to this runtime process's + own environment. + """ + flag_overrides: dict[str, bool] | None = None + """Flags forced to a value after every other layer, keyed by flag name.""" + + is_staff_github: bool | None = None + """GitHub-staff membership. Omit when it was never recorded: a legacy staff account then + counts as both staff populations and keeps every staff feature. + """ + is_staff_microsoft: bool | None = None + """Microsoft-staff membership. Omitted the same way as `isStaffGitHub`.""" + + @staticmethod + def from_dict(obj: Any) -> '_FeatureFlagsResolveRequest': + assert isinstance(obj, dict) + is_experimental = from_bool(obj.get("isExperimental")) + is_staff = from_bool(obj.get("isStaff")) + is_team = from_bool(obj.get("isTeam")) + base_flags = from_union([lambda x: from_dict(from_bool, x), from_none], obj.get("baseFlags")) + config = obj.get("config") + environment = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("environment")) + flag_overrides = from_union([lambda x: from_dict(from_bool, x), from_none], obj.get("flagOverrides")) + is_staff_github = from_union([from_bool, from_none], obj.get("isStaffGithub")) + is_staff_microsoft = from_union([from_bool, from_none], obj.get("isStaffMicrosoft")) + return _FeatureFlagsResolveRequest(is_experimental, is_staff, is_team, base_flags, config, environment, flag_overrides, is_staff_github, is_staff_microsoft) + + def to_dict(self) -> dict: + result: dict = {} + result["isExperimental"] = from_bool(self.is_experimental) + result["isStaff"] = from_bool(self.is_staff) + result["isTeam"] = from_bool(self.is_team) + if self.base_flags is not None: + result["baseFlags"] = from_union([lambda x: from_dict(from_bool, x), from_none], self.base_flags) + if self.config is not None: + result["config"] = self.config + if self.environment is not None: + result["environment"] = from_union([lambda x: from_dict(from_str, x), from_none], self.environment) + if self.flag_overrides is not None: + result["flagOverrides"] = from_union([lambda x: from_dict(from_bool, x), from_none], self.flag_overrides) + if self.is_staff_github is not None: + result["isStaffGithub"] = from_union([from_bool, from_none], self.is_staff_github) + if self.is_staff_microsoft is not None: + result["isStaffMicrosoft"] = from_union([from_bool, from_none], self.is_staff_microsoft) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _FeatureFlagsResolveStartupGatesRequest: + """Caller population and startup inputs to resolve named online-experiment gates for. + Carries no account: the runtime resolves the assignment for the authentication it already + holds. + """ + flags: list[str] + """The feature flags to resolve. A name this runtime does not declare is reported as an + error rather than as a disabled flag. + """ + is_experimental: bool + """Whether the caller is in an experimental population.""" + + is_staff: bool + """Whether the caller is GitHub or Microsoft staff.""" + + is_team: bool + """Whether the caller belongs to a team plan.""" + + refresh_without_session: bool + """Whether the runtime may refresh the cached assignment over the network without an active + session. The refresh never blocks this call: the answer comes from the cache either way, + so a refresh only affects a later launch. + """ + streamer_mode: bool + """Whether streamer mode is on, which withholds the populations an experiment would + otherwise segment on. + """ + config: Any = None + """User settings document whose `featureFlags` block is layered on top of the resolved set.""" + + environment: dict[str, str] | None = None + """Environment to read flag overrides from. Defaults to this runtime process's own + environment. + """ + first_launch_at_millis: float | None = None + """When this installation first launched, in milliseconds since the Unix epoch. Experiments + that segment on installation age read it. Omit when the first launch was never recorded. + """ + is_staff_github: bool | None = None + """GitHub-staff membership. Omit when it was never recorded: a legacy staff account then + counts as both staff populations and keeps every staff feature. + """ + is_staff_microsoft: bool | None = None + """Microsoft-staff membership. Omitted the same way as `isStaffGitHub`.""" + + @staticmethod + def from_dict(obj: Any) -> '_FeatureFlagsResolveStartupGatesRequest': + assert isinstance(obj, dict) + flags = from_list(from_str, obj.get("flags")) + is_experimental = from_bool(obj.get("isExperimental")) + is_staff = from_bool(obj.get("isStaff")) + is_team = from_bool(obj.get("isTeam")) + refresh_without_session = from_bool(obj.get("refreshWithoutSession")) + streamer_mode = from_bool(obj.get("streamerMode")) + config = obj.get("config") + environment = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("environment")) + first_launch_at_millis = from_union([from_float, from_none], obj.get("firstLaunchAtMillis")) + is_staff_github = from_union([from_bool, from_none], obj.get("isStaffGithub")) + is_staff_microsoft = from_union([from_bool, from_none], obj.get("isStaffMicrosoft")) + return _FeatureFlagsResolveStartupGatesRequest(flags, is_experimental, is_staff, is_team, refresh_without_session, streamer_mode, config, environment, first_launch_at_millis, is_staff_github, is_staff_microsoft) + + def to_dict(self) -> dict: + result: dict = {} + result["flags"] = from_list(from_str, self.flags) + result["isExperimental"] = from_bool(self.is_experimental) + result["isStaff"] = from_bool(self.is_staff) + result["isTeam"] = from_bool(self.is_team) + result["refreshWithoutSession"] = from_bool(self.refresh_without_session) + result["streamerMode"] = from_bool(self.streamer_mode) + if self.config is not None: + result["config"] = self.config + if self.environment is not None: + result["environment"] = from_union([lambda x: from_dict(from_str, x), from_none], self.environment) + if self.first_launch_at_millis is not None: + result["firstLaunchAtMillis"] = from_union([to_float, from_none], self.first_launch_at_millis) + if self.is_staff_github is not None: + result["isStaffGithub"] = from_union([from_bool, from_none], self.is_staff_github) + if self.is_staff_microsoft is not None: + result["isStaffMicrosoft"] = from_union([from_bool, from_none], self.is_staff_microsoft) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class FleetStartRequest: @@ -4262,6 +4555,110 @@ def to_dict(self) -> dict: result["trusted"] = from_bool(self.trusted) return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _FolderTrustIsTrustedParams: + """A folder to check against a persisted trusted-folders list, and the configuration + directory holding that list. + """ + path: str + """Folder to check. A folder counts as trusted when it is itself listed, when an ancestor is + listed, or when its git root is listed. + """ + config_dir: str | None = None + """Configuration directory to read the trusted-folders list from. Omit it to read the + default location; supply it when the host has redirected its configuration, or the check + answers from a list the host is not using. + """ + + @staticmethod + def from_dict(obj: Any) -> '_FolderTrustIsTrustedParams': + assert isinstance(obj, dict) + path = from_str(obj.get("path")) + config_dir = from_union([from_str, from_none], obj.get("configDir")) + return _FolderTrustIsTrustedParams(path, config_dir) + + def to_dict(self) -> dict: + result: dict = {} + result["path"] = from_str(self.path) + if self.config_dir is not None: + result["configDir"] = from_union([from_str, from_none], self.config_dir) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _ForceLoginOrgsDecision: + """The verdict of the `forceLoginOrgs` enterprise sign-in pin for one credential. A managed + device may pin Copilot to an approved set of GitHub organizations so a developer cannot + sign in with a personal — or another enterprise's — account and escape every control that + keys off the signed-in identity. A host must refuse to proceed when `allowed` is false; + the remaining fields explain the refusal rather than qualifying it. + + The enterprise sign-in verdict for the credential this resolution ran against, evaluated + over the effective policy so a server-delivered pin is honored alongside a device one. + Computed on every resolution, including one that was not retained, so a host that + declines to apply policy still cannot proceed past a pin. + """ + allowed: bool + """Whether the client may proceed. True whenever no pin is in force, so an unmanaged host is + never blocked. + """ + enforced: bool + """Whether a pin applies at all. Distinguishes "allowed because unmanaged" from "allowed + because the account is approved", which a diagnostics surface needs and which also + decides whether a bring-your-own-key provider may serve inference. + """ + policy_diagnostics: list[str] + """Error-level detail from managed-policy discovery — which file failed to load, which field + failed validation. Carried on the decision rather than left to the log because a refusal + can exit the process before a log sink exists, which would discard the only actionable + diagnostic a misconfigured managed device produces. + """ + required_orgs: list[str] + """The approved organization logins, as configured. Enterprise configuration rather than + user data, so it is safe to name in a message or a log line. + """ + denial: str | None = None + """A stable kebab-case reason code when refused: `org-not-approved`, + `unverifiable-credential`, `policy-undetermined`, `policy-invalid`, `not-signed-in`, or + `membership-unknown`. Omitted when allowed. Treat an unrecognized code as a refusal + rather than mapping it to an allow: a code this host has not been taught about still + denied the credential. + """ + matched_org: str | None = None + """The approved organization the account matched, when allowed under a pin.""" + + message: str | None = None + """A developer-facing explanation of the refusal. Omitted when allowed.""" + + @staticmethod + def from_dict(obj: Any) -> '_ForceLoginOrgsDecision': + assert isinstance(obj, dict) + allowed = from_bool(obj.get("allowed")) + enforced = from_bool(obj.get("enforced")) + policy_diagnostics = from_list(from_str, obj.get("policyDiagnostics")) + required_orgs = from_list(from_str, obj.get("requiredOrgs")) + denial = from_union([from_str, from_none], obj.get("denial")) + matched_org = from_union([from_str, from_none], obj.get("matchedOrg")) + message = from_union([from_str, from_none], obj.get("message")) + return _ForceLoginOrgsDecision(allowed, enforced, policy_diagnostics, required_orgs, denial, matched_org, message) + + def to_dict(self) -> dict: + result: dict = {} + result["allowed"] = from_bool(self.allowed) + result["enforced"] = from_bool(self.enforced) + result["policyDiagnostics"] = from_list(from_str, self.policy_diagnostics) + result["requiredOrgs"] = from_list(from_str, self.required_orgs) + if self.denial is not None: + result["denial"] = from_union([from_str, from_none], self.denial) + if self.matched_org is not None: + result["matchedOrg"] = from_union([from_str, from_none], self.matched_org) + if self.message is not None: + result["message"] = from_union([from_str, from_none], self.message) + return result + class GhCLIAuthInfoType(Enum): GH_CLI = "gh-cli" @@ -4412,7 +4809,7 @@ def to_dict(self) -> dict: # Experimental: this type is part of an experimental API and may change or be removed. # Internal: this type is an internal SDK API and is not part of the public surface. @dataclass -class GitHubOwnersRequestIDResult: +class _GitHubOwnersRequestIDResult: """A freshly registered request id. Registering it before the listing starts is what lets a cancel that races the request still find the owner listing slot. The id serves one listing only. Long-abandoned unused ids can be released by later allocations. @@ -4421,10 +4818,10 @@ class GitHubOwnersRequestIDResult: """Request id to pass to `gitHubOwners.list` and, to abandon it, `gitHubOwners.cancel`.""" @staticmethod - def from_dict(obj: Any) -> 'GitHubOwnersRequestIDResult': + def from_dict(obj: Any) -> '_GitHubOwnersRequestIDResult': assert isinstance(obj, dict) request_id = from_int(obj.get("requestId")) - return GitHubOwnersRequestIDResult(request_id) + return _GitHubOwnersRequestIDResult(request_id) def to_dict(self) -> dict: result: dict = {} @@ -5085,6 +5482,13 @@ class HooksDiscoverRequest: still contribute to each remaining row's effective enabled state. This filters sources rather than simulating a host with no settings. """ + hook_directories: list[str] | None = field(default=None, kw_only=True) + """Explicit discovery-only file-hook roots, separate from callback hooks and filesystem + grants. Available without ambient discovery or repository file-hook enablement, subject + to managed/hook disables. Relative paths resolve against the request's effective working + directory when supplied. Null and omission are equivalent; an empty array contributes no + explicit roots. + """ project_paths: list[str] | None = None """Optional project directory paths whose trusted repository and project-expanded plugin hooks should be discovered. When omitted or empty, user, managed-policy, and globally @@ -5095,13 +5499,16 @@ class HooksDiscoverRequest: def from_dict(obj: Any) -> 'HooksDiscoverRequest': assert isinstance(obj, dict) exclude_host_hooks = from_union([from_bool, from_none], obj.get("excludeHostHooks")) + hook_directories = from_union([lambda x: from_list(from_str, x), from_none], obj.get("hookDirectories")) project_paths = from_union([lambda x: from_list(from_str, x), from_none], obj.get("projectPaths")) - return HooksDiscoverRequest(exclude_host_hooks, project_paths) + return HooksDiscoverRequest(exclude_host_hooks, project_paths, hook_directories=hook_directories) def to_dict(self) -> dict: result: dict = {} if self.exclude_host_hooks is not None: result["excludeHostHooks"] = from_union([from_bool, from_none], self.exclude_host_hooks) + if self.hook_directories is not None: + result["hookDirectories"] = from_union([lambda x: from_list(from_str, x), from_none], self.hook_directories) if self.project_paths is not None: result["projectPaths"] = from_union([lambda x: from_list(from_str, x), from_none], self.project_paths) return result @@ -5995,6 +6402,63 @@ class InstructionSourceType(Enum): REPO = "repo" VSCODE = "vscode" +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _InstructionsDiscoverRepoSourcesRequest: + """Explicit discovery arguments for one location, supplied by the caller rather than derived + from a session. + """ + location: str + """Repository location to discover. An empty string selects no repository scope; cwdPath can + still supply working-directory sources. + """ + additional_dirs: list[str] | None = None + """Extra directories to scan alongside instruction directories configured in the runtime + environment. + """ + config_dir: str | None = None + """Copilot configuration directory for user-level sources. An omitted or empty value uses + the server's configuration home. + """ + cwd_path: str | None = None + """Working directory whose instruction sources should also be included. Omission adds no + separate working-directory scope. + """ + enable_child_instructions: bool | None = None + """When true, also discover instruction files nested below the location.""" + + is_cli: bool | None = None + """When true, include the sources a command-line host reads. Hosts that are not the CLI omit + it. + """ + + @staticmethod + def from_dict(obj: Any) -> '_InstructionsDiscoverRepoSourcesRequest': + assert isinstance(obj, dict) + location = from_str(obj.get("location")) + additional_dirs = from_union([lambda x: from_list(from_str, x), from_none], obj.get("additionalDirs")) + config_dir = from_union([from_str, from_none], obj.get("configDir")) + cwd_path = from_union([from_str, from_none], obj.get("cwdPath")) + enable_child_instructions = from_union([from_bool, from_none], obj.get("enableChildInstructions")) + is_cli = from_union([from_bool, from_none], obj.get("isCli")) + return _InstructionsDiscoverRepoSourcesRequest(location, additional_dirs, config_dir, cwd_path, enable_child_instructions, is_cli) + + def to_dict(self) -> dict: + result: dict = {} + result["location"] = from_str(self.location) + if self.additional_dirs is not None: + result["additionalDirs"] = from_union([lambda x: from_list(from_str, x), from_none], self.additional_dirs) + if self.config_dir is not None: + result["configDir"] = from_union([from_str, from_none], self.config_dir) + if self.cwd_path is not None: + result["cwdPath"] = from_union([from_str, from_none], self.cwd_path) + if self.enable_child_instructions is not None: + result["enableChildInstructions"] = from_union([from_bool, from_none], self.enable_child_instructions) + if self.is_cli is not None: + result["isCli"] = from_union([from_bool, from_none], self.is_cli) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class InstructionsDiscoverRequest: @@ -6004,6 +6468,12 @@ class InstructionsDiscoverRequest: """When true, omit the host's instruction sources (user/home-level files and plugin rules), leaving only repository and working-directory sources. For multitenant deployments. """ + instruction_directories: list[str] | None = field(default=None, kw_only=True) + """Explicit discovery-only instruction roots using existing SDK instruction formats, not + filesystem grants. Available without ambient discovery, subject to instruction/managed + disables. Relative paths resolve against the request's effective working directory when + supplied. Null and omission are equivalent; an empty array contributes no explicit roots. + """ project_paths: list[str] | None = None """Optional list of project directory paths to scan for repository/working-directory instruction sources. When omitted or empty, only user-level and plugin instruction @@ -6014,13 +6484,16 @@ class InstructionsDiscoverRequest: def from_dict(obj: Any) -> 'InstructionsDiscoverRequest': assert isinstance(obj, dict) exclude_host_instructions = from_union([from_bool, from_none], obj.get("excludeHostInstructions")) + instruction_directories = from_union([lambda x: from_list(from_str, x), from_none], obj.get("instructionDirectories")) project_paths = from_union([lambda x: from_list(from_str, x), from_none], obj.get("projectPaths")) - return InstructionsDiscoverRequest(exclude_host_instructions, project_paths) + return InstructionsDiscoverRequest(exclude_host_instructions, project_paths, instruction_directories=instruction_directories) def to_dict(self) -> dict: result: dict = {} if self.exclude_host_instructions is not None: result["excludeHostInstructions"] = from_union([from_bool, from_none], self.exclude_host_instructions) + if self.instruction_directories is not None: + result["instructionDirectories"] = from_union([lambda x: from_list(from_str, x), from_none], self.instruction_directories) if self.project_paths is not None: result["projectPaths"] = from_union([lambda x: from_list(from_str, x), from_none], self.project_paths) return result @@ -6034,6 +6507,12 @@ class InstructionsGetDiscoveryPathsRequest: """When true, omit the host's user-level instruction targets, leaving only repository targets. For multitenant deployments (mirrors `discover`'s `excludeHostInstructions`). """ + instruction_directories: list[str] | None = field(default=None, kw_only=True) + """Explicit discovery-only instruction roots to enumerate, including configured missing + directories. Relative paths resolve against the request's effective working directory + when supplied. These are not filesystem grants or preferred creation targets. Null and + omission are equivalent. + """ project_paths: list[str] | None = None """Optional list of project directory paths. When omitted or empty, only the user-level targets are returned. @@ -6043,17 +6522,40 @@ class InstructionsGetDiscoveryPathsRequest: def from_dict(obj: Any) -> 'InstructionsGetDiscoveryPathsRequest': assert isinstance(obj, dict) exclude_host_instructions = from_union([from_bool, from_none], obj.get("excludeHostInstructions")) + instruction_directories = from_union([lambda x: from_list(from_str, x), from_none], obj.get("instructionDirectories")) project_paths = from_union([lambda x: from_list(from_str, x), from_none], obj.get("projectPaths")) - return InstructionsGetDiscoveryPathsRequest(exclude_host_instructions, project_paths) + return InstructionsGetDiscoveryPathsRequest(exclude_host_instructions, project_paths, instruction_directories=instruction_directories) def to_dict(self) -> dict: result: dict = {} if self.exclude_host_instructions is not None: result["excludeHostInstructions"] = from_union([from_bool, from_none], self.exclude_host_instructions) + if self.instruction_directories is not None: + result["instructionDirectories"] = from_union([lambda x: from_list(from_str, x), from_none], self.instruction_directories) if self.project_paths is not None: result["projectPaths"] = from_union([lambda x: from_list(from_str, x), from_none], self.project_paths) return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _InstructionsInitPromptResult: + """The prompt the init command runs.""" + + prompt: str + """Prompt text authoring repository instructions.""" + + @staticmethod + def from_dict(obj: Any) -> '_InstructionsInitPromptResult': + assert isinstance(obj, dict) + prompt = from_str(obj.get("prompt")) + return _InstructionsInitPromptResult(prompt) + + def to_dict(self) -> dict: + result: dict = {} + result["prompt"] = from_str(self.prompt) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class InterruptMainTurnRequest: @@ -6376,6 +6878,26 @@ class HostType(Enum): ADO = "ado" GITHUB = "github" +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _LocationPermissionsCleanupResult: + """Which stored location permissions were dropped because their directory is gone.""" + + removed_location_keys: list[str] + """Location keys whose grants were removed. Empty when every stored location still exists.""" + + @staticmethod + def from_dict(obj: Any) -> '_LocationPermissionsCleanupResult': + assert isinstance(obj, dict) + removed_location_keys = from_list(from_str, obj.get("removedLocationKeys")) + return _LocationPermissionsCleanupResult(removed_location_keys) + + def to_dict(self) -> dict: + result: dict = {} + result["removedLocationKeys"] = from_list(from_str, self.removed_location_keys) + return result + # Experimental: this type is part of an experimental API and may change or be removed. class SessionLogLevel(Enum): """Log severity level. Determines how the message is displayed in the timeline. Defaults to @@ -6483,6 +7005,33 @@ def to_dict(self) -> dict: result["tools"] = from_union([lambda x: from_list(from_str, x), from_none], self.tools) return result +# Experimental: this type is part of an experimental API and may change or be removed. +class ManagedPermissionVerdict(Enum): + """Managed-policy decision only; never bypasses user permissions or other security + controls. + + Managed-policy verdict only: deny blocks the URL, ask requires approval, allow approves + under managed policy, and unmanaged leaves the normal consumer permission flow in effect. + No verdict bypasses other security controls. + """ + ALLOW = "allow" + ASK = "ask" + DENY = "deny" + UNMANAGED = "unmanaged" + +# Experimental: this type is part of an experimental API and may change or be removed. +class ManagedPluginRetryStatus(Enum): + """What the retry did for this plugin. + + What retrying did for one plugin required by managed settings. + """ + ALREADY_PRESENT = "already_present" + DEFERRED = "deferred" + FAILED = "failed" + INSTALLED = "installed" + NOT_REQUIRED = "not_required" + UPDATED = "updated" + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ManagedSettingMeta: @@ -6526,6 +7075,86 @@ def to_dict(self) -> dict: result["requested"] = from_union([from_str, from_none], self.requested) return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _ManagedSettingsApplyControlsParams: + """A user settings document and the effective managed policy whose controlled entries should + be stamped onto it. + """ + settings: Any + """The user settings document to stamp. Entries the managed layer does not name are returned + exactly as supplied. + """ + managed: Any = None + """The effective managed policy, as composed by `managedSettings.resolveEffectiveValue`. + Omit it on an unmanaged host: the settings then come back unchanged. + """ + + @staticmethod + def from_dict(obj: Any) -> '_ManagedSettingsApplyControlsParams': + assert isinstance(obj, dict) + settings = obj.get("settings") + managed = obj.get("managed") + return _ManagedSettingsApplyControlsParams(settings, managed) + + def to_dict(self) -> dict: + result: dict = {} + result["settings"] = self.settings + if self.managed is not None: + result["managed"] = self.managed + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _ManagedSettingsApplyControlsResult: + """The user settings with the managed layer's controlled entries stamped on.""" + + settings_json: Any + """The resulting settings document.""" + + @staticmethod + def from_dict(obj: Any) -> '_ManagedSettingsApplyControlsResult': + assert isinstance(obj, dict) + settings_json = obj.get("settingsJson") + return _ManagedSettingsApplyControlsResult(settings_json) + + def to_dict(self) -> dict: + result: dict = {} + result["settingsJson"] = self.settings_json + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +class _ManagedSettingsBypassDisableSource(Enum): + """Which layer disabled bypass-permissions mode. The layer matters because only a + server-delivered disable is pinned to an account and can later be lifted by signing in + elsewhere. + + Which layer currently disables bypass mode. Omit it when nothing does. + + Which layer disables bypass mode after this resolution. Omitted when none does, which is + what a lifted disable looks like. + """ + MANAGED = "managed" + MDM = "mdm" + USER = "user" + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +class _ManagedSettingsBypassPolicyResolution(Enum): + """What the managed-policy resolution said about bypass-permissions mode. `unknown` is a + third answer rather than a missing one, and the caller must send it explicitly when it + has no verdict. + + What the resolution said about bypass mode. Send `unknown` rather than omitting the field + when there is no verdict. + """ + ALLOWED = "allowed" + DISABLED = "disabled" + UNKNOWN = "unknown" + # Experimental: this type is part of an experimental API and may change or be removed. class ManagedSettingsChannel(Enum): """A channel accepted by managedSettings.compose. @@ -6590,16 +7219,18 @@ class ManagedSettingsResolvedData: shape as `session.managedSettings.get`, excluding session-local injection. """ bypass_permissions_disabled: bool - """Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. - Deny-wins across layers, and forced on when `failClosed` is true. + """Whether an explicit enterprise policy restriction disables bypass-permissions ("yolo") + mode for this session. Deny-wins across layers; an unresolved policy does not force this + on by itself. """ device_managed: bool """Whether an actual device MDM/plist/registry/file managed-settings layer was present""" fail_closed: bool - """Whether managed policy could not be determined (e.g. a failed server fetch) and the - session fell back to the fail-closed restriction. When true, restrictions such as - disabling bypass-permissions are enforced even though `settings` may be absent. + """Whether managed policy could not be determined (e.g. a failed server fetch) and + unresolved-policy safeguards remain active. This does not by itself disable + bypass-permissions; `bypassPermissionsDisabled` reports only an explicit policy + restriction. """ managed_keys: list[str] """The setting keys under enterprise management in the effective managed settings (e.g. @@ -6620,6 +7251,11 @@ class ManagedSettingsResolvedData: """Whether at least two managed sources supplied permission allowlists, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. """ + permissions_context: ManagedPermissionsContext | None = None + """Reusable retained managed permission policy for managedSettings.permissions.evaluate, + including source composition and session-local injection. Provided by live session + snapshots; absent in older events. Refresh the context when managed policy changes. + """ policy_helper_managed: bool | None = None """Whether the policy-helper managed-settings layer was present. The policy helper is the weakest channel: it fills keys no enterprise source set and can never replace one. @@ -6646,10 +7282,11 @@ def from_dict(obj: Any) -> 'ManagedSettingsResolvedData': source = ManagedSettingsResolvedSource(obj.get("source")) client_managed = from_union([from_bool, from_none], obj.get("clientManaged")) permissions_allow_intersected = from_union([from_bool, from_none], obj.get("permissionsAllowIntersected")) + permissions_context = from_union([ManagedPermissionsContext.from_dict, from_none], obj.get("permissionsContext")) policy_helper_managed = from_union([from_bool, from_none], obj.get("policyHelperManaged")) sandbox_enabled_by_undetermined_policy = from_union([from_bool, from_none], obj.get("sandboxEnabledByUndeterminedPolicy")) settings = obj.get("settings") - return ManagedSettingsResolvedData(bypass_permissions_disabled, device_managed, fail_closed, managed_keys, server_managed, source, client_managed, permissions_allow_intersected, policy_helper_managed, sandbox_enabled_by_undetermined_policy, settings) + return ManagedSettingsResolvedData(bypass_permissions_disabled, device_managed, fail_closed, managed_keys, server_managed, source, client_managed, permissions_allow_intersected, permissions_context, policy_helper_managed, sandbox_enabled_by_undetermined_policy, settings) def to_dict(self) -> dict: result: dict = {} @@ -6663,6 +7300,8 @@ def to_dict(self) -> dict: result["clientManaged"] = from_union([from_bool, from_none], self.client_managed) if self.permissions_allow_intersected is not None: result["permissionsAllowIntersected"] = from_union([from_bool, from_none], self.permissions_allow_intersected) + if self.permissions_context is not None: + result["permissionsContext"] = from_union([lambda x: to_class(ManagedPermissionsContext, x), from_none], self.permissions_context) if self.policy_helper_managed is not None: result["policyHelperManaged"] = from_union([from_bool, from_none], self.policy_helper_managed) if self.sandbox_enabled_by_undetermined_policy is not None: @@ -6721,6 +7360,186 @@ def to_dict(self) -> dict: result["model"] = from_union([from_str, from_none], self.model) return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +class _ManagedSettingsPolicyHelperReason(Enum): + """The policy-helper invocation reason. Defaults to sessionStart. + + The operation that requires a policy-helper response. + """ + PLUGIN_ACTIVATION = "pluginActivation" + SESSION_START = "sessionStart" + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +class _ManagedSettingsServerPolicyCacheMode(Enum): + """How this ingestion may use the on-disk server-policy cache. Defaults to the runtime's own + policy when omitted. + + How an ingestion may use the on-disk server-policy cache. + + How this resolution may use the on-disk server-policy cache. Defaults to the runtime's + own policy when omitted. Rejected when combined with `bypassCacheRead`, because the two + express the same decision and accepting both would leave the caller unsure which one the + runtime honored. + """ + CACHE_ONLY = "cacheOnly" + DISABLED = "disabled" + FALLBACK_ONLY = "fallbackOnly" + PREFER_FRESH = "preferFresh" + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +class _ManagedSettingsLogLevel(Enum): + """Severity of this diagnostic. + + Severity of a diagnostic emitted while ingesting managed settings. + """ + DEBUG = "debug" + ERROR = "error" + INFO = "info" + WARNING = "warning" + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +class _ManagedSettingsPolicyHelperAuthority(Enum): + """Which channel registered the policy helper that produced the helper layer. + + Which channel registered the policy helper that produced a helper layer. + """ + DEVICE = "device" + SERVER = "server" + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +class _ManagedSettingsServerPolicyResolution(Enum): + """How the server layer was resolved. + + How the server layer of an ingestion was resolved. + + How the current runtime resolved the server layer. + """ + CACHE_MISS = "cacheMiss" + FAIL_CLOSED = "failClosed" + FAIL_OPEN = "failOpen" + FALLBACK_CACHE = "fallbackCache" + FRESH_CACHE = "freshCache" + LIVE = "live" + SKIPPED_NO_TOKEN = "skippedNoToken" + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _ManagedSettingsLoadRemoteControlResult: + """The managed remote-control policy in force for an account, if any. An absent policy means + the account is unmanaged for this capability, which a host reads as allowed — so absence + here is a positive statement that no policy applies, never a stand-in for a resolution + that failed. A resolution that cannot be completed is rejected instead. + """ + remote_control: Any = None + """The account's `remoteControl` policy document. Absent when no managed policy governs + remote control for this account. Reported as an explicit disabled policy, rather than as + absence, when the service refused to deliver a policy this client is too old to enforce. + """ + + @staticmethod + def from_dict(obj: Any) -> '_ManagedSettingsLoadRemoteControlResult': + assert isinstance(obj, dict) + remote_control = obj.get("remoteControl") + return _ManagedSettingsLoadRemoteControlResult(remote_control) + + def to_dict(self) -> dict: + result: dict = {} + if self.remote_control is not None: + result["remoteControl"] = self.remote_control + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _ManagedSettingsParseClientValueParams: + """The client-supplied managed settings to validate and normalize. Omitting them is valid + and yields no policy. + """ + settings: Any = None + """The managed settings a client is supplying for a session it is about to open. Omit them + when the client is imposing no policy of its own. + """ + + @staticmethod + def from_dict(obj: Any) -> '_ManagedSettingsParseClientValueParams': + assert isinstance(obj, dict) + settings = obj.get("settings") + return _ManagedSettingsParseClientValueParams(settings) + + def to_dict(self) -> dict: + result: dict = {} + if self.settings is not None: + result["settings"] = self.settings + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _ManagedSettingsParseClientValueResult: + """The accepted managed settings, reduced to the keys a client is allowed to set.""" + + settings_json: Any = None + """The normalized settings. Omitted when the client supplied none. An accepted document that + sets nothing comes back as an empty object, which is distinct from omission. + """ + + @staticmethod + def from_dict(obj: Any) -> '_ManagedSettingsParseClientValueResult': + assert isinstance(obj, dict) + settings_json = obj.get("settingsJson") + return _ManagedSettingsParseClientValueResult(settings_json) + + def to_dict(self) -> dict: + result: dict = {} + if self.settings_json is not None: + result["settingsJson"] = self.settings_json + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _ManagedSettingsReadDeviceResult: + """The device-managed settings layer, with the sandbox floor and the load outcome a host + needs to compose it into an effective policy. + """ + load_failed: bool + """Whether discovery or validation failed. A host must treat this as "policy unknown" rather + than "no policy": absent settings with this set false mean the device genuinely has none. + """ + sandbox_floor: Any = None + """Sandbox floor read from the discovered document before validation, so a document that is + otherwise rejected can still raise the floor it declared. + """ + settings: Any = None + """Validated, canonical device-managed settings. Omitted when no device policy was + discovered, when the discovered document failed validation, or when it validated to an + empty object — an empty document carries no policy, and reporting it as present would + outrank layers that do. + """ + + @staticmethod + def from_dict(obj: Any) -> '_ManagedSettingsReadDeviceResult': + assert isinstance(obj, dict) + load_failed = from_bool(obj.get("loadFailed")) + sandbox_floor = obj.get("sandboxFloor") + settings = obj.get("settings") + return _ManagedSettingsReadDeviceResult(load_failed, sandbox_floor, settings) + + def to_dict(self) -> dict: + result: dict = {} + result["loadFailed"] = from_bool(self.load_failed) + if self.sandbox_floor is not None: + result["sandboxFloor"] = self.sandbox_floor + if self.settings is not None: + result["settings"] = self.settings + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ManagedSettingsReadResult: @@ -6749,6 +7568,105 @@ def to_dict(self) -> dict: result["settingsJson"] = self.settings_json return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _ManagedSettingsResolveDeviceForceLoginOrgsGateParams: + """The credential and environment to evaluate the device-channel sign-in pin against.""" + + environment: Any + """The host's environment variables. Read for the variables that would make an identity's + membership snapshot forgeable, so a credential whose provenance cannot be trusted is + refused rather than accepted. + """ + auth_info: Any = None + """The authenticated identity to judge. Omitted when signed out, which is the case the + device channel exists to serve: it can refuse a first sign-in before any credential + exists. + """ + + @staticmethod + def from_dict(obj: Any) -> '_ManagedSettingsResolveDeviceForceLoginOrgsGateParams': + assert isinstance(obj, dict) + environment = obj.get("environment") + auth_info = obj.get("authInfo") + return _ManagedSettingsResolveDeviceForceLoginOrgsGateParams(environment, auth_info) + + def to_dict(self) -> dict: + result: dict = {} + result["environment"] = self.environment + if self.auth_info is not None: + result["authInfo"] = self.auth_info + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _ManagedSettingsResolveEffectiveValueParams: + """The managed-settings layers to compose into an effective policy. Every layer is optional; + omitting all of them yields no effective policy. + """ + device_sandbox_floor: Any = None + """The device sandbox floor, folded into the device layer under `sandbox` before composition.""" + + mdm: Any = None + """The device layer: native MDM and managed-file settings describing the machine rather than + the account. + """ + policy_helper: Any = None + """The policy-helper layer, an enforced channel sourced from a platform policy helper + process. + """ + server: Any = None + """The account's server-delivered org policy, as returned by a managed-settings fetch. + Omitted before sign-in or when the fetch has not landed. + """ + + @staticmethod + def from_dict(obj: Any) -> '_ManagedSettingsResolveEffectiveValueParams': + assert isinstance(obj, dict) + device_sandbox_floor = obj.get("deviceSandboxFloor") + mdm = obj.get("mdm") + policy_helper = obj.get("policyHelper") + server = obj.get("server") + return _ManagedSettingsResolveEffectiveValueParams(device_sandbox_floor, mdm, policy_helper, server) + + def to_dict(self) -> dict: + result: dict = {} + if self.device_sandbox_floor is not None: + result["deviceSandboxFloor"] = self.device_sandbox_floor + if self.mdm is not None: + result["mdm"] = self.mdm + if self.policy_helper is not None: + result["policyHelper"] = self.policy_helper + if self.server is not None: + result["server"] = self.server + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _ManagedSettingsResolveEffectiveValueResult: + """The effective managed policy composed from the supplied layers.""" + + settings_json: Any = None + """The composed effective managed settings. Omitted when no layer sets anything, so an + unmanaged host locks nothing. Carries `sandboxAdvisoryFloorDeclined: true` when an + advisory server force-on was dropped because this host cannot run the sandbox. + """ + + @staticmethod + def from_dict(obj: Any) -> '_ManagedSettingsResolveEffectiveValueResult': + assert isinstance(obj, dict) + settings_json = obj.get("settingsJson") + return _ManagedSettingsResolveEffectiveValueResult(settings_json) + + def to_dict(self) -> dict: + result: dict = {} + if self.settings_json is not None: + result["settingsJson"] = self.settings_json + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ManagedSettingsResolveRequest: @@ -6790,6 +7708,119 @@ def to_dict(self) -> dict: result["workingDirectory"] = from_union([from_str, from_none], self.working_directory) return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _ManagedSettingsRetainPolicyParams: + """The managed layers to retain as this process's startup policy, with the outcome of + fetching each one. + """ + device_load_failed: bool + """Whether the device load failed. Report it honestly, for the same reason as + `serverFetchFailed`. + """ + device_sandbox_undetermined: bool + """Whether the device sandbox intent is unreadable independently of its other settings.""" + + device_source_found: bool + """Whether a device policy source demonstrably exists. This, not a read error, is the + evidence a device is managed, so a fail-closed policy gates on it. + """ + managed_policy_present: bool + """Whether any managed policy source exists, independently of fetch failures.""" + + permission_policy_undetermined: bool + """Whether permission policy is undetermined. Sandbox-only uncertainty does not imply this + flag. + """ + preserve_server_state: bool + """Preserve the retained server layer during a device-only resolution.""" + + sandbox_enabled_by_undetermined_policy: bool + """Whether uncertainty alone enabled the shared sandbox floor.""" + + sandbox_floor_undetermined: bool + """Whether the shared sandbox policy is undetermined. Diagnostic errors alone do not imply + this flag. + """ + server_fetch_failed: bool + """Whether the server fetch failed. Report it honestly: true keeps the already-retained + server layer rather than replacing it with an absent one. + """ + server_forced_refresh_failure: bool + """Whether a required fresh server-policy confirmation failed.""" + + server_source_found: bool + """Whether the server policy source exists, even when its response is unavailable.""" + + device_response: Any = None + """The device/MDM policy layer.""" + + device_sandbox_floor: Any = None + """Sandbox floor declared by the device layer, carried separately so a document that failed + validation can still raise the floor. + """ + policy_helper_response: Any = None + """The policy helper's response: the third managed layer that a process owning its startup + policy resolves. It is retained with the other two so that every ambient read in this + process, including the mutation guards and the governed marketplace allowlist that the + `plugins.*` handlers derive, sees the whole policy the process fetched. A session-owning + process omits it, because it resolves a helper per session against that session's working + directory. + """ + server_response: Any = None + """The resolved server policy. A known source can lack a response after a forced-refresh + failure. + """ + shared_sandbox_floor: Any = None + """Shared server/device sandbox floor, excluding session-local helper policy.""" + + @staticmethod + def from_dict(obj: Any) -> '_ManagedSettingsRetainPolicyParams': + assert isinstance(obj, dict) + device_load_failed = from_bool(obj.get("deviceLoadFailed")) + device_sandbox_undetermined = from_bool(obj.get("deviceSandboxUndetermined")) + device_source_found = from_bool(obj.get("deviceSourceFound")) + managed_policy_present = from_bool(obj.get("managedPolicyPresent")) + permission_policy_undetermined = from_bool(obj.get("permissionPolicyUndetermined")) + preserve_server_state = from_bool(obj.get("preserveServerState")) + sandbox_enabled_by_undetermined_policy = from_bool(obj.get("sandboxEnabledByUndeterminedPolicy")) + sandbox_floor_undetermined = from_bool(obj.get("sandboxFloorUndetermined")) + server_fetch_failed = from_bool(obj.get("serverFetchFailed")) + server_forced_refresh_failure = from_bool(obj.get("serverForcedRefreshFailure")) + server_source_found = from_bool(obj.get("serverSourceFound")) + device_response = obj.get("deviceResponse") + device_sandbox_floor = obj.get("deviceSandboxFloor") + policy_helper_response = obj.get("policyHelperResponse") + server_response = obj.get("serverResponse") + shared_sandbox_floor = obj.get("sharedSandboxFloor") + return _ManagedSettingsRetainPolicyParams(device_load_failed, device_sandbox_undetermined, device_source_found, managed_policy_present, permission_policy_undetermined, preserve_server_state, sandbox_enabled_by_undetermined_policy, sandbox_floor_undetermined, server_fetch_failed, server_forced_refresh_failure, server_source_found, device_response, device_sandbox_floor, policy_helper_response, server_response, shared_sandbox_floor) + + def to_dict(self) -> dict: + result: dict = {} + result["deviceLoadFailed"] = from_bool(self.device_load_failed) + result["deviceSandboxUndetermined"] = from_bool(self.device_sandbox_undetermined) + result["deviceSourceFound"] = from_bool(self.device_source_found) + result["managedPolicyPresent"] = from_bool(self.managed_policy_present) + result["permissionPolicyUndetermined"] = from_bool(self.permission_policy_undetermined) + result["preserveServerState"] = from_bool(self.preserve_server_state) + result["sandboxEnabledByUndeterminedPolicy"] = from_bool(self.sandbox_enabled_by_undetermined_policy) + result["sandboxFloorUndetermined"] = from_bool(self.sandbox_floor_undetermined) + result["serverFetchFailed"] = from_bool(self.server_fetch_failed) + result["serverForcedRefreshFailure"] = from_bool(self.server_forced_refresh_failure) + result["serverSourceFound"] = from_bool(self.server_source_found) + if self.device_response is not None: + result["deviceResponse"] = self.device_response + if self.device_sandbox_floor is not None: + result["deviceSandboxFloor"] = self.device_sandbox_floor + if self.policy_helper_response is not None: + result["policyHelperResponse"] = self.policy_helper_response + if self.server_response is not None: + result["serverResponse"] = self.server_response + if self.shared_sandbox_floor is not None: + result["sharedSandboxFloor"] = self.shared_sandbox_floor + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ManagedSettingsValidateRequest: @@ -7295,7 +8326,7 @@ def to_dict(self) -> dict: # Experimental: this type is part of an experimental API and may change or be removed. # Internal: this type is an internal SDK API and is not part of the public surface. @dataclass -class MCPConfigureGitHubRequest: +class _MCPConfigureGitHubRequest: """Credential-free authentication identity used to configure GitHub MCP.""" auth_info: Any @@ -7304,10 +8335,10 @@ class MCPConfigureGitHubRequest: """ @staticmethod - def from_dict(obj: Any) -> 'MCPConfigureGitHubRequest': + def from_dict(obj: Any) -> '_MCPConfigureGitHubRequest': assert isinstance(obj, dict) auth_info = obj.get("authInfo") - return MCPConfigureGitHubRequest(auth_info) + return _MCPConfigureGitHubRequest(auth_info) def to_dict(self) -> dict: result: dict = {} @@ -7717,6 +8748,28 @@ def to_dict(self) -> dict: result["running"] = from_bool(self.running) return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _MCPListBuiltinServerNamesResult: + """Names of the MCP servers the runtime supplies itself.""" + + names: list[str] + """Every built-in server name. A host disabling the built-ins wholesale suppresses exactly + this set. + """ + + @staticmethod + def from_dict(obj: Any) -> '_MCPListBuiltinServerNamesResult': + assert isinstance(obj, dict) + names = from_list(from_str, obj.get("names")) + return _MCPListBuiltinServerNamesResult(names) + + def to_dict(self) -> dict: + result: dict = {} + result["names"] = from_list(from_str, self.names) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MCPListToolsRequest: @@ -8282,6 +9335,94 @@ def to_dict(self) -> dict: result["cursor"] = from_union([from_str, from_none], self.cursor) return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _MCPRegistryCancelRequest: + """Registry search to abandon.""" + + request_id: int + """Request ID from `mcp.registry.allocateRequestId` that the search uses.""" + + @staticmethod + def from_dict(obj: Any) -> '_MCPRegistryCancelRequest': + assert isinstance(obj, dict) + request_id = from_int(obj.get("requestId")) + return _MCPRegistryCancelRequest(request_id) + + def to_dict(self) -> dict: + result: dict = {} + result["requestId"] = from_int(self.request_id) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _MCPRegistryCancelResult: + """Whether the cancel reached a live search.""" + + canceled: bool + """True when the cancel stopped a running search. False for unknown or reclaimed IDs, + completed or canceled searches, and unused reservations. The cancel releases an unused + reservation. + """ + + @staticmethod + def from_dict(obj: Any) -> '_MCPRegistryCancelResult': + assert isinstance(obj, dict) + canceled = from_bool(obj.get("canceled")) + return _MCPRegistryCancelResult(canceled) + + def to_dict(self) -> dict: + result: dict = {} + result["canceled"] = from_bool(self.canceled) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _MCPRegistryRequestIDResult: + """Request id naming a cancellable registry search.""" + + request_id: int + """Request ID for `mcp.registry.search` and `mcp.registry.cancel`. It serves one search. + Allocation can reclaim unused IDs at the 1,024-ID reservation limit. + """ + + @staticmethod + def from_dict(obj: Any) -> '_MCPRegistryRequestIDResult': + assert isinstance(obj, dict) + request_id = from_int(obj.get("requestId")) + return _MCPRegistryRequestIDResult(request_id) + + def to_dict(self) -> dict: + result: dict = {} + result["requestId"] = from_int(self.request_id) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _MCPRegistrySearchResult: + """Servers selected from the registry response.""" + + servers: Any + """The server objects, carried opaquely. The runtime follows pages, keeps the newest entry + per server name, cuts the list to `limit`, and sorts an empty-query result by GitHub + stars. Each server object remains unchanged because the registry owns that shape. + """ + + @staticmethod + def from_dict(obj: Any) -> '_MCPRegistrySearchResult': + assert isinstance(obj, dict) + servers = obj.get("servers") + return _MCPRegistrySearchResult(servers) + + def to_dict(self) -> dict: + result: dict = {} + result["servers"] = self.servers + return result + class MCPServerConfigType(Enum): """Local transport type. Defaults to stdio when omitted. @@ -8300,7 +9441,7 @@ class MCPServerConfigType(Enum): # Experimental: this type is part of an experimental API and may change or be removed. # Internal: this type is an internal SDK API and is not part of the public surface. @dataclass -class MCPReloadWithConfigRequest: +class _MCPReloadWithConfigRequest: """Opaque MCP reload configuration.""" config: Any @@ -8309,10 +9450,10 @@ class MCPReloadWithConfigRequest: """ @staticmethod - def from_dict(obj: Any) -> 'MCPReloadWithConfigRequest': + def from_dict(obj: Any) -> '_MCPReloadWithConfigRequest': assert isinstance(obj, dict) config = obj.get("config") - return MCPReloadWithConfigRequest(config) + return _MCPReloadWithConfigRequest(config) def to_dict(self) -> dict: result: dict = {} @@ -8434,7 +9575,7 @@ class MCPServerConfigHTTPType(Enum): # Experimental: this type is part of an experimental API and may change or be removed. # Internal: this type is an internal SDK API and is not part of the public surface. -class MCPServerConfigMemoryType(Enum): +class _MCPServerConfigMemoryType(Enum): """In-process MCP transport type.""" MEMORY = "memory" @@ -8466,6 +9607,38 @@ class MCPSetEnvValueModeDetails(Enum): DIRECT = "direct" INDIRECT = "indirect" +# Experimental: this type is part of an experimental API and may change or be removed. +class OptionsUpdateToolFilterPrecedence(Enum): + """Controls how availableTools (allowlist) and excludedTools (denylist) combine when both + are set. + """ + AVAILABLE = "available" + EXCLUDED = "excluded" + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _MCPShouldExcludeGitHubToolsResult: + """Whether the gh-replaceable GitHub MCP tools may be clipped for the session described by + the request. + """ + exclude_gh_replaceable_tools: bool + """True only when both halves hold: the session's filters still reach the platform shell + tool, and the host actually has the `gh` those tools would be replaced by. Feed it + straight back as the `excludeGhReplaceableTools` build option. + """ + + @staticmethod + def from_dict(obj: Any) -> '_MCPShouldExcludeGitHubToolsResult': + assert isinstance(obj, dict) + exclude_gh_replaceable_tools = from_bool(obj.get("excludeGhReplaceableTools")) + return _MCPShouldExcludeGitHubToolsResult(exclude_gh_replaceable_tools) + + def to_dict(self) -> dict: + result: dict = {} + result["excludeGhReplaceableTools"] = from_bool(self.exclude_gh_replaceable_tools) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MCPStopServerRequest: @@ -8516,17 +9689,17 @@ def to_dict(self) -> dict: # Experimental: this type is part of an experimental API and may change or be removed. # Internal: this type is an internal SDK API and is not part of the public surface. @dataclass -class MCPUnregisterExternalClientRequest: +class _MCPUnregisterExternalClientRequest: """Server name identifying the external client to remove.""" server_name: str """Server name of the external client to unregister""" @staticmethod - def from_dict(obj: Any) -> 'MCPUnregisterExternalClientRequest': + def from_dict(obj: Any) -> '_MCPUnregisterExternalClientRequest': assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) - return MCPUnregisterExternalClientRequest(server_name) + return _MCPUnregisterExternalClientRequest(server_name) def to_dict(self) -> dict: result: dict = {} @@ -9323,6 +10496,92 @@ def to_dict(self) -> dict: result["cleared"] = from_bool(self.cleared) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class ModelGetReasoningSummariesResult: + """Reasoning-summaries presentation policy for the session's current model.""" + + clear_redundant_setting: bool + """Whether the supplied `persistedShowReasoning` setting only restates the model default. + This reports a redundancy, not a standing instruction to delete the setting: it stays + true for as long as the setting and the model default agree, including for a user who + later chose that value deliberately. It exists for a one-time migration of a legacy + setting written before the model default made it redundant, so a host that acts on it + owns the cleanup and must gate it behind its own persisted `done` marker, delete only a + setting whose value still matches the one it read, and never repeat the deletion on a + later launch. Under an off-by-default reasoning-summaries experiment an explicit + `showReasoning: true` opt-in also reads as redundant on a summary-producing model, so an + ungated host would keep deleting that opt-in and hide summaries again on every launch. + """ + experiment_settled: bool + """Whether the session's reasoning-summaries experiment assignment has settled. False means + this read does not wait for a pending assignment and reports the experiment-derived + fields conservatively: `showHiddenHint` is false and `visibility` falls back to + `persistedShowReasoning`, or is omitted when none was supplied. A host that wants the + assigned answer re-reads until this is true. + """ + model_default_shows_summaries: bool + """Whether the selected model shows reasoning summaries by default.""" + + ready: bool + """False until the session has a model catalog and a selected model. This read never + resolves the catalog itself, and the runtime emits no readiness notification, so + repeating this call alone leaves `ready` false indefinitely on a session that has neither + listed models nor run a turn. `session.model.list` is the prerequisite and the signal: it + resolves and caches the catalog for the session, so a successful return means the next + read is ready. The exception is a session that resolves models without a Copilot catalog, + that is a BYOK registry or an alternate provider: `session.model.list` returns an empty + list without caching a catalog, so `ready` stays false and this policy is unavailable + there. While false, every model-policy field is false and `visibility` is omitted; + `experimentSettled` is independent of readiness and still reports the assignment state. + """ + session_shows_summaries: bool + """Whether turns on this session produce reasoning summaries, honouring an explicit session + reasoning-summary override. + """ + show_hidden_hint: bool + """Whether to show the hint that reasoning summaries are hidden. Always false while + `experimentSettled` is false. + """ + show_reasoning_headers: bool + """Whether the selected model emits reasoning headers.""" + + visibility: bool | None = None + """Default reasoning visibility, and only meaningful once `ready` is true: false when the + session produces no summaries, else the persisted setting, else the experiment's default. + While `ready` is true it is omitted only when all three are unavailable, that is when the + session produces summaries, no `persistedShowReasoning` was supplied, and + `experimentSettled` is false. While `ready` is false there is no model policy to report + and this field is always omitted, so an absent value alone does not imply a pending + experiment. + """ + + @staticmethod + def from_dict(obj: Any) -> 'ModelGetReasoningSummariesResult': + assert isinstance(obj, dict) + clear_redundant_setting = from_bool(obj.get("clearRedundantSetting")) + experiment_settled = from_bool(obj.get("experimentSettled")) + model_default_shows_summaries = from_bool(obj.get("modelDefaultShowsSummaries")) + ready = from_bool(obj.get("ready")) + session_shows_summaries = from_bool(obj.get("sessionShowsSummaries")) + show_hidden_hint = from_bool(obj.get("showHiddenHint")) + show_reasoning_headers = from_bool(obj.get("showReasoningHeaders")) + visibility = from_union([from_bool, from_none], obj.get("visibility")) + return ModelGetReasoningSummariesResult(clear_redundant_setting, experiment_settled, model_default_shows_summaries, ready, session_shows_summaries, show_hidden_hint, show_reasoning_headers, visibility) + + def to_dict(self) -> dict: + result: dict = {} + result["clearRedundantSetting"] = from_bool(self.clear_redundant_setting) + result["experimentSettled"] = from_bool(self.experiment_settled) + result["modelDefaultShowsSummaries"] = from_bool(self.model_default_shows_summaries) + result["ready"] = from_bool(self.ready) + result["sessionShowsSummaries"] = from_bool(self.session_shows_summaries) + result["showHiddenHint"] = from_bool(self.show_hidden_hint) + result["showReasoningHeaders"] = from_bool(self.show_reasoning_headers) + if self.visibility is not None: + result["visibility"] = from_union([from_bool, from_none], self.visibility) + return result + # Experimental: this type is part of an experimental API and may change or be removed. class ModelProviderAutomaticDiscoveryMode(Enum): """Whether automatic discovery is allowed, limited to configured providers, or @@ -9892,14 +11151,6 @@ class OptionsUpdateContextTier(Enum): DEFAULT = "default" LONG_CONTEXT = "long_context" -# Experimental: this type is part of an experimental API and may change or be removed. -class OptionsUpdateToolFilterPrecedence(Enum): - """Controls how availableTools (allowlist) and excludedTools (denylist) combine when both - are set. - """ - AVAILABLE = "available" - EXCLUDED = "excluded" - # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class PendingPermissionRequest: @@ -10427,6 +11678,62 @@ def to_dict(self) -> dict: result["success"] = from_bool(self.success) return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _PermissionsEvaluateManagedPolicyParams: + """The verbatim managed `permissions` slice to interpret.""" + + permissions: Any = None + """The `permissions` object from a managed settings layer, for example + `{"disableBypassPermissionsMode":"disable"}`. Omitted or null means no managed policy + applies. + """ + + @staticmethod + def from_dict(obj: Any) -> '_PermissionsEvaluateManagedPolicyParams': + assert isinstance(obj, dict) + permissions = obj.get("permissions") + return _PermissionsEvaluateManagedPolicyParams(permissions) + + def to_dict(self) -> dict: + result: dict = {} + if self.permissions is not None: + result["permissions"] = self.permissions + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class PermissionsEvaluateManagedPolicyResult: + """The policy questions a host asks before granting elevated permissions.""" + + bypass_permissions_disabled: bool + """True when managed policy forbids bypass-permissions mode, so allow-all style flags must + not take effect. + """ + permission_rules_active: bool + """True when the managed layer supplies permission rules that the session must enforce.""" + + policy_undetermined: bool + """True when a managed policy exists but this build could not determine its meaning. Hosts + fail closed on it rather than assuming permission was granted. + """ + + @staticmethod + def from_dict(obj: Any) -> 'PermissionsEvaluateManagedPolicyResult': + assert isinstance(obj, dict) + bypass_permissions_disabled = from_bool(obj.get("bypassPermissionsDisabled")) + permission_rules_active = from_bool(obj.get("permissionRulesActive")) + policy_undetermined = from_bool(obj.get("policyUndetermined")) + return PermissionsEvaluateManagedPolicyResult(bypass_permissions_disabled, permission_rules_active, policy_undetermined) + + def to_dict(self) -> dict: + result: dict = {} + result["bypassPermissionsDisabled"] = from_bool(self.bypass_permissions_disabled) + result["permissionRulesActive"] = from_bool(self.permission_rules_active) + result["policyUndetermined"] = from_bool(self.policy_undetermined) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class PermissionsFolderTrustAddTrustedResult: @@ -10755,6 +12062,66 @@ def to_dict(self) -> dict: result["success"] = from_bool(self.success) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class PersistenceWriteRawJSONLockedRequest: + """A complete JSON snapshot to write under the runtime's per-path file lock.""" + + data_json: str + """The serialized JSON document to write. It replaces the file's contents entirely; nothing + is merged. + """ + path: str + """Absolute path of the file to replace. Parent directories are created when missing.""" + + file_header: str | None = None + """Comment lines to emit above the document, for a format that tolerates them. Omit for + strict JSON. + """ + + @staticmethod + def from_dict(obj: Any) -> 'PersistenceWriteRawJSONLockedRequest': + assert isinstance(obj, dict) + data_json = from_str(obj.get("dataJson")) + path = from_str(obj.get("path")) + file_header = from_union([from_none, from_str], obj.get("fileHeader")) + return PersistenceWriteRawJSONLockedRequest(data_json, path, file_header) + + def to_dict(self) -> dict: + result: dict = {} + result["dataJson"] = from_str(self.data_json) + result["path"] = from_str(self.path) + if self.file_header is not None: + result["fileHeader"] = from_union([from_none, from_str], self.file_header) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class PersistenceWriteRawJSONLockedResult: + """The outcome of a locked snapshot write.""" + + success: bool + """Whether the file now holds the requested contents.""" + + error: str | None = None + """Why the write failed. Absent when `success` is true. A lock the runtime could not acquire + in time reports here rather than raising. + """ + + @staticmethod + def from_dict(obj: Any) -> 'PersistenceWriteRawJSONLockedResult': + assert isinstance(obj, dict) + success = from_bool(obj.get("success")) + error = from_union([from_none, from_str], obj.get("error")) + return PersistenceWriteRawJSONLockedResult(success, error) + + def to_dict(self) -> dict: + result: dict = {} + result["success"] = from_bool(self.success) + if self.error is not None: + result["error"] = from_union([from_none, from_str], self.error) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class PingRequest: @@ -12467,9 +13834,182 @@ def to_dict(self) -> dict: result["branch"] = from_union([from_str, from_none], self.branch) return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +class _SettingsEditOperation(Enum): + """Set a scalar, add or remove a string list item, or remove the setting. The handler + rejects a missing or incompatible value. + + One atomic settings mutation. + """ + ADD_ITEM = "add-item" + REMOVE = "remove" + REMOVE_ITEM = "remove-item" + SET = "set" + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +class _RepoSettingsEditScope(Enum): + """Shared or local repository settings file. + + Which repository settings file to edit. + """ + LOCAL = "local" + REPO = "repo" + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _RepoSettingsReadRawRequest: + """Which repository settings file to read.""" + + repo_root: str + """Absolute path of the repository root.""" + + scope: str + """Use repo for shared settings or local for settings.local.json. Other scopes are rejected.""" + + @staticmethod + def from_dict(obj: Any) -> '_RepoSettingsReadRawRequest': + assert isinstance(obj, dict) + repo_root = from_str(obj.get("repoRoot")) + scope = from_str(obj.get("scope")) + return _RepoSettingsReadRawRequest(repo_root, scope) + + def to_dict(self) -> dict: + result: dict = {} + result["repoRoot"] = from_str(self.repo_root) + result["scope"] = from_str(self.scope) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _RepoSettingsReadRawResult: + """Parsed repository settings without normalization or layering.""" + + config: Any + """Stored JSON values after the existing JSONC parser and prototype-safety checks. Empty + when the file is missing, blank, or invalid. + """ + invalid: bool + """True when the file exists but did not parse, so `config` is empty for that reason rather + than because nothing is set. + """ + + @staticmethod + def from_dict(obj: Any) -> '_RepoSettingsReadRawResult': + assert isinstance(obj, dict) + config = obj.get("config") + invalid = from_bool(obj.get("invalid")) + return _RepoSettingsReadRawResult(config, invalid) + + def to_dict(self) -> dict: + result: dict = {} + result["config"] = self.config + result["invalid"] = from_bool(self.invalid) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _RepoSettingsWriteAllRequest: + """The complete set of repository-overridable settings to store.""" + + repo_root: str + """Absolute path of the repository root.""" + + scope: str + """Use repo for shared settings or local for settings.local.json. Other scopes are rejected.""" + + settings: Any + """Object of allowed settings to store. Missing allowlisted keys are removed. Other input + keys are ignored, while unknown stored keys are preserved. + """ + + @staticmethod + def from_dict(obj: Any) -> '_RepoSettingsWriteAllRequest': + assert isinstance(obj, dict) + repo_root = from_str(obj.get("repoRoot")) + scope = from_str(obj.get("scope")) + settings = obj.get("settings") + return _RepoSettingsWriteAllRequest(repo_root, scope, settings) + + def to_dict(self) -> dict: + result: dict = {} + result["repoRoot"] = from_str(self.repo_root) + result["scope"] = from_str(self.scope) + result["settings"] = self.settings + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _RepoSettingsWriteKeyRequest: + """One key to write or remove in a repository settings file.""" + + key: str + """Canonical top-level key permitted by the repository settings allowlist.""" + + repo_root: str + """Absolute path of the repository root.""" + + scope: str + """Use repo for shared settings or local for settings.local.json. Other scopes are rejected.""" + + delete: bool | None = None + """True to remove the key instead of writing it.""" + + value: Any = UNSET_SENTINEL + """Value to validate and write. Required unless delete is true; a null value is validated, + not treated as deletion. + """ + + @staticmethod + def from_dict(obj: Any) -> '_RepoSettingsWriteKeyRequest': + assert isinstance(obj, dict) + key = from_str(obj.get("key")) + repo_root = from_str(obj.get("repoRoot")) + scope = from_str(obj.get("scope")) + delete = from_union([from_bool, from_none], obj.get("delete")) + value = obj["value"] if "value" in obj else UNSET_SENTINEL + return _RepoSettingsWriteKeyRequest(key, repo_root, scope, delete, value) + + def to_dict(self) -> dict: + result: dict = {} + result["key"] = from_str(self.key) + result["repoRoot"] = from_str(self.repo_root) + result["scope"] = from_str(self.scope) + if self.delete is not None: + result["delete"] = from_union([from_bool, from_none], self.delete) + if self.value is not UNSET_SENTINEL: + result["value"] = self.value + return result + class ResponseFormatType(Enum): JSON_SCHEMA = "json_schema" +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SandboxApplyDefaultsRequest: + """The stored sandbox settings to resolve against the defaults.""" + + stored: Any + """The sandbox settings as stored in the settings file. An empty object resolves to the + defaults alone. + """ + + @staticmethod + def from_dict(obj: Any) -> 'SandboxApplyDefaultsRequest': + assert isinstance(obj, dict) + stored = obj.get("stored") + return SandboxApplyDefaultsRequest(stored) + + def to_dict(self) -> dict: + result: dict = {} + result["stored"] = self.stored + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SandboxConfigAuth: @@ -12679,6 +14219,34 @@ class SandboxConfigSource(Enum): USER_DISABLED = "user_disabled" USER_ENABLED = "user_enabled" +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SandboxCredentialSuggestion: + """A possible secret-bearing environment variable and optional locally suggested HTTPS + injection hosts. This is a draft for user review, not an active grant. + """ + name: str + """Environment variable name. The secret value is never returned.""" + + suggested_inject_hosts: list[str] + """HTTPS hostnames suggested by a local known-provider mapping. An empty list requires the + user to supply injection hosts before adding a masking entry. These suggestions do not + allow network access. + """ + + @staticmethod + def from_dict(obj: Any) -> 'SandboxCredentialSuggestion': + assert isinstance(obj, dict) + name = from_str(obj.get("name")) + suggested_inject_hosts = from_list(from_str, obj.get("suggestedInjectHosts")) + return SandboxCredentialSuggestion(name, suggested_inject_hosts) + + def to_dict(self) -> dict: + result: dict = {} + result["name"] = from_str(self.name) + result["suggestedInjectHosts"] = from_list(from_str, self.suggested_inject_hosts) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SandboxDisableForSessionResult: @@ -12703,6 +14271,102 @@ def to_dict(self) -> dict: result["success"] = from_bool(self.success) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SandboxDocsURLResult: + """The documentation URL a host cites when it declines or downgrades a sandbox setting.""" + + url: str + """Documentation URL describing the sandbox and the settings that control it.""" + + @staticmethod + def from_dict(obj: Any) -> 'SandboxDocsURLResult': + assert isinstance(obj, dict) + url = from_str(obj.get("url")) + return SandboxDocsURLResult(url) + + def to_dict(self) -> dict: + result: dict = {} + result["url"] = from_str(self.url) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _SandboxEffectiveForOptionalParams: + """A sandbox config and the per-feature routing flag to resolve it for.""" + + routing_flag: str + """The routing flag naming the feature being launched, such as `sandboxMcpServers` or + `sandboxLspServers`. + """ + sandbox: Any = None + """The session's resolved `sandbox` slice. Omitted when the host has none, which yields a + disabled sandbox rather than an error. + """ + + @staticmethod + def from_dict(obj: Any) -> '_SandboxEffectiveForOptionalParams': + assert isinstance(obj, dict) + routing_flag = from_str(obj.get("routingFlag")) + sandbox = obj.get("sandbox") + return _SandboxEffectiveForOptionalParams(routing_flag, sandbox) + + def to_dict(self) -> dict: + result: dict = {} + result["routingFlag"] = from_str(self.routing_flag) + if self.sandbox is not None: + result["sandbox"] = self.sandbox + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _SandboxEffectiveForOptionalResult: + """The sandbox a named feature actually launches under.""" + + sandbox_json: Any + """The effective sandbox config for the requested feature, or a disabled sandbox when the + feature is routed out of it. + """ + + @staticmethod + def from_dict(obj: Any) -> '_SandboxEffectiveForOptionalResult': + assert isinstance(obj, dict) + sandbox_json = obj.get("sandboxJson") + return _SandboxEffectiveForOptionalResult(sandbox_json) + + def to_dict(self) -> dict: + result: dict = {} + result["sandboxJson"] = self.sandbox_json + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _SandboxEnforcementCommandAllowedParams: + """One slash command, as typed, to test against the enforcement-block allowlist.""" + + args: list[str] + """The command's arguments, in order. Pass an empty array for a bare command: several + entries in the allowlist admit the bare form and refuse every argument. + """ + command_name: str + """The command name as the user typed it, with or without its leading slash.""" + + @staticmethod + def from_dict(obj: Any) -> '_SandboxEnforcementCommandAllowedParams': + assert isinstance(obj, dict) + args = from_list(from_str, obj.get("args")) + command_name = from_str(obj.get("commandName")) + return _SandboxEnforcementCommandAllowedParams(args, command_name) + + def to_dict(self) -> dict: + result: dict = {} + result["args"] = from_list(from_str, self.args) + result["commandName"] = from_str(self.command_name) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SandboxEnforcementStatus: @@ -12733,6 +14397,54 @@ def to_dict(self) -> dict: result["reason"] = from_union([from_str, from_none], self.reason) return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _SandboxPickerCandidate: + """One file-picker suggestion offered up for the sandbox filesystem check.""" + + is_directory: bool + """Whether the suggestion is a directory. A directory the policy does not itself allow may + still be needed as a waypoint to an allowed subtree. + """ + path: str + """Absolute path the suggestion would attach.""" + + @staticmethod + def from_dict(obj: Any) -> '_SandboxPickerCandidate': + assert isinstance(obj, dict) + is_directory = from_bool(obj.get("isDirectory")) + path = from_str(obj.get("path")) + return _SandboxPickerCandidate(is_directory, path) + + def to_dict(self) -> dict: + result: dict = {} + result["isDirectory"] = from_bool(self.is_directory) + result["path"] = from_str(self.path) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _SandboxFilterPickerCandidatesResult: + """Per-candidate allow decisions, positionally aligned with the request.""" + + allowed: list[bool] + """One decision per candidate, in the same order. Always the same length as the request's + `candidates`. + """ + + @staticmethod + def from_dict(obj: Any) -> '_SandboxFilterPickerCandidatesResult': + assert isinstance(obj, dict) + allowed = from_list(from_bool, obj.get("allowed")) + return _SandboxFilterPickerCandidatesResult(allowed) + + def to_dict(self) -> dict: + result: dict = {} + result["allowed"] = from_list(from_bool, self.allowed) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SandboxGrantPathForRequestResult: @@ -12802,6 +14514,58 @@ def to_dict(self) -> dict: result["reason"] = from_union([from_str, from_none], self.reason) return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _SandboxPreparePickerPolicyParams: + """The working directory and sandbox settings to build a file-picker policy from.""" + + cwd: str + """The session's working directory. Git discovery and grant canonicalization are resolved + against it. + """ + sandbox: Any = None + """The session's resolved `sandbox` slice. Omitted, or disabled, yields no policy and every + candidate is kept. + """ + + @staticmethod + def from_dict(obj: Any) -> '_SandboxPreparePickerPolicyParams': + assert isinstance(obj, dict) + cwd = from_str(obj.get("cwd")) + sandbox = obj.get("sandbox") + return _SandboxPreparePickerPolicyParams(cwd, sandbox) + + def to_dict(self) -> dict: + result: dict = {} + result["cwd"] = from_str(self.cwd) + if self.sandbox is not None: + result["sandbox"] = self.sandbox + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _SandboxPreparePickerPolicyResult: + """The prepared file-picker policy, or none when the sandbox is disabled.""" + + sandbox_json: Any = None + """The prepared policy to pass back to `sandbox.filterPickerCandidates`. Absent when the + sandbox is disabled or absent, in which case filtering keeps every candidate. + """ + + @staticmethod + def from_dict(obj: Any) -> '_SandboxPreparePickerPolicyResult': + assert isinstance(obj, dict) + sandbox_json = obj.get("sandboxJson") + return _SandboxPreparePickerPolicyResult(sandbox_json) + + def to_dict(self) -> dict: + result: dict = {} + if self.sandbox_json is not None: + result["sandboxJson"] = self.sandbox_json + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SandboxProxyCACreateResult: @@ -12832,6 +14596,148 @@ class SandboxProxyCAState(Enum): NOT_INSTALLED = "notInstalled" UNSUPPORTED = "unsupported" +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SandboxProxySecretGetResult: + """The stored sandbox proxy password, if one is present.""" + + value: str | None = None + """The stored password. Absent when no entry exists in either the keychain or the on-disk + fallback store. + """ + + @staticmethod + def from_dict(obj: Any) -> 'SandboxProxySecretGetResult': + assert isinstance(obj, dict) + value = from_union([from_str, from_none], obj.get("value")) + return SandboxProxySecretGetResult(value) + + def to_dict(self) -> dict: + result: dict = {} + if self.value is not None: + result["value"] = from_union([from_str, from_none], self.value) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SandboxProxySecretLocation: + """Where this runtime keeps the sandbox proxy password.""" + + config_dir: str + """Absolute path of the credential store holding the sandbox proxy password. The proxy + password belongs to no MCP server, so it is kept in its own directory rather than in the + MCP secret store. + """ + secret_id: str + """Identifier of the proxy password entry within that store. Derived from the configuration + directory, so two Copilot homes on one machine never share an entry. Use it to recognize + a `${secret:…}` reference in stored settings as this runtime's own proxy password. + """ + + @staticmethod + def from_dict(obj: Any) -> 'SandboxProxySecretLocation': + assert isinstance(obj, dict) + config_dir = from_str(obj.get("configDir")) + secret_id = from_str(obj.get("secretId")) + return SandboxProxySecretLocation(config_dir, secret_id) + + def to_dict(self) -> dict: + result: dict = {} + result["configDir"] = from_str(self.config_dir) + result["secretId"] = from_str(self.secret_id) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SandboxProxySecretSaveRequest: + """The sandbox proxy password to store.""" + + value: str + """The password to store for the sandbox proxy.""" + + @staticmethod + def from_dict(obj: Any) -> 'SandboxProxySecretSaveRequest': + assert isinstance(obj, dict) + value = from_str(obj.get("value")) + return SandboxProxySecretSaveRequest(value) + + def to_dict(self) -> dict: + result: dict = {} + result["value"] = from_str(self.value) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SandboxProxySecretSaveResult: + """Where the sandbox proxy password was stored.""" + + stored_in_keychain: bool + """Whether the OS keychain accepted the value. False means it was written to the on-disk + fallback store instead, which is still readable by `sandbox.proxySecret.get`. + """ + + @staticmethod + def from_dict(obj: Any) -> 'SandboxProxySecretSaveResult': + assert isinstance(obj, dict) + stored_in_keychain = from_bool(obj.get("storedInKeychain")) + return SandboxProxySecretSaveResult(stored_in_keychain) + + def to_dict(self) -> dict: + result: dict = {} + result["storedInKeychain"] = from_bool(self.stored_in_keychain) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _SandboxResolveFlooredConfigParams: + """The user's sandbox settings and the managed sandbox floor to raise them to.""" + + managed_sandbox: Any = None + """The effective managed `sandbox` slice acting as a floor. Omitted when no administrator + policy applies, in which case the user's own settings stand unchanged. + """ + user_sandbox: Any = None + """The user's own `sandbox` settings slice. Omitted when the user has configured none, which + still yields the managed floor rather than a disabled sandbox. + """ + + @staticmethod + def from_dict(obj: Any) -> '_SandboxResolveFlooredConfigParams': + assert isinstance(obj, dict) + managed_sandbox = obj.get("managedSandbox") + user_sandbox = obj.get("userSandbox") + return _SandboxResolveFlooredConfigParams(managed_sandbox, user_sandbox) + + def to_dict(self) -> dict: + result: dict = {} + if self.managed_sandbox is not None: + result["managedSandbox"] = self.managed_sandbox + if self.user_sandbox is not None: + result["userSandbox"] = self.user_sandbox + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SandboxResolveFlooredConfigResult: + """The user's sandbox settings raised to the managed floor.""" + + sandbox_json: Any + """The floored sandbox config, normalized through the declared sandbox shape so only + declared fields in a fixed order are present. + """ + + @staticmethod + def from_dict(obj: Any) -> 'SandboxResolveFlooredConfigResult': + assert isinstance(obj, dict) + sandbox_json = obj.get("sandboxJson") + return SandboxResolveFlooredConfigResult(sandbox_json) + + def to_dict(self) -> dict: + result: dict = {} + result["sandboxJson"] = self.sandbox_json + return result + # Experimental: this type is part of an experimental API and may change or be removed. class SandboxSessionChange(Enum): """A session-scoped sandbox transition applied while handling a slash command @@ -12843,6 +14749,63 @@ class SandboxSessionChange(Enum): DISABLED = "disabled" RESTORED = "restored" +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _SandboxUnsupportedStartupNoticeParams: + """The managed sandbox policy in force, which selects which remedy the notice offers.""" + + enabled_by_undetermined_policy: bool | None = None + """True when the sandbox stayed on only because the managed policy could not be determined. + Distinguishes a policy that positively enforces the sandbox from one that merely failed + to resolve, which is the difference between telling the user to contact an administrator + and telling them to wait. Defaults to false. + """ + managed_sandbox: Any = None + """The managed `sandbox` slice, when a policy supplies one. The notice treats the sandbox as + enforced only when this both enables it and sets `failIfUnavailable`, because a managed + sandbox that tolerates being unavailable still leaves the user able to turn it off. + """ + + @staticmethod + def from_dict(obj: Any) -> '_SandboxUnsupportedStartupNoticeParams': + assert isinstance(obj, dict) + enabled_by_undetermined_policy = from_union([from_bool, from_none], obj.get("enabledByUndeterminedPolicy")) + managed_sandbox = obj.get("managedSandbox") + return _SandboxUnsupportedStartupNoticeParams(enabled_by_undetermined_policy, managed_sandbox) + + def to_dict(self) -> dict: + result: dict = {} + if self.enabled_by_undetermined_policy is not None: + result["enabledByUndeterminedPolicy"] = from_union([from_bool, from_none], self.enabled_by_undetermined_policy) + if self.managed_sandbox is not None: + result["managedSandbox"] = self.managed_sandbox + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _SandboxUnsupportedStartupNoticeResult: + """The startup warning, absent when the machine can run a sandbox.""" + + notice: str | None = None + """The user-facing warning, carrying the platform remedy and the recovery the policy in + force actually allows. Absent when this machine supports a sandbox, so a host can call + unconditionally and print only what it gets. + """ + + @staticmethod + def from_dict(obj: Any) -> '_SandboxUnsupportedStartupNoticeResult': + assert isinstance(obj, dict) + notice = from_union([from_str, from_none], obj.get("notice")) + return _SandboxUnsupportedStartupNoticeResult(notice) + + def to_dict(self) -> dict: + result: dict = {} + if self.notice is not None: + result["notice"] = from_union([from_str, from_none], self.notice) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ScheduleAddAtRequest: @@ -14664,6 +16627,26 @@ def to_dict(self) -> dict: result["reloadMcp"] = from_union([from_bool, from_none], self.reload_mcp) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SessionPluginsRetryManagedRequest: + plugins: list[str] | None = None + """"plugin@marketplace" specs to retry. Omit to retry every plugin the session's managed + settings require. + """ + + @staticmethod + def from_dict(obj: Any) -> 'SessionPluginsRetryManagedRequest': + assert isinstance(obj, dict) + plugins = from_union([lambda x: from_list(from_str, x), from_none], obj.get("plugins")) + return SessionPluginsRetryManagedRequest(plugins) + + def to_dict(self) -> dict: + result: dict = {} + if self.plugins is not None: + result["plugins"] = from_union([lambda x: from_list(from_str, x), from_none], self.plugins) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SessionPruneResult: @@ -16552,7 +18535,11 @@ class SkillsDiscoverRequest: """Optional list of project directory paths to scan for project-scoped skills""" skill_directories: list[str] | None = None - """Optional list of additional skill directory paths to include""" + """Explicit discovery-only skill roots, not filesystem grants. Available without ambient + discovery, subject to skill/managed disables. Relative paths resolve against the + request's effective working directory when supplied. Null and omission are equivalent; an + empty array contributes no explicit roots. + """ @staticmethod def from_dict(obj: Any) -> 'SkillsDiscoverRequest': @@ -16610,6 +18597,12 @@ class SkillsGetDiscoveryPathsRequest: """Optional list of project directory paths. When omitted or empty, only personal and custom directories are returned. """ + skill_directories: list[str] | None = field(default=None, kw_only=True) + """Explicit discovery-only skill roots to enumerate, including configured missing + directories. Relative paths resolve against the request's effective working directory + when supplied. These are not filesystem grants or preferred creation targets. Null and + omission are equivalent. + """ @staticmethod def from_dict(obj: Any) -> 'SkillsGetDiscoveryPathsRequest': @@ -16617,7 +18610,8 @@ def from_dict(obj: Any) -> 'SkillsGetDiscoveryPathsRequest': exclude_host_skills = from_union([from_bool, from_none], obj.get("excludeHostSkills")) ignored_skills_locations = from_union([lambda x: from_list(from_str, x), from_none], obj.get("ignoredSkillsLocations")) project_paths = from_union([lambda x: from_list(from_str, x), from_none], obj.get("projectPaths")) - return SkillsGetDiscoveryPathsRequest(exclude_host_skills, ignored_skills_locations, project_paths) + skill_directories = from_union([lambda x: from_list(from_str, x), from_none], obj.get("skillDirectories")) + return SkillsGetDiscoveryPathsRequest(exclude_host_skills, ignored_skills_locations, project_paths, skill_directories=skill_directories) def to_dict(self) -> dict: result: dict = {} @@ -16627,6 +18621,8 @@ def to_dict(self) -> dict: result["ignoredSkillsLocations"] = from_union([lambda x: from_list(from_str, x), from_none], self.ignored_skills_locations) if self.project_paths is not None: result["projectPaths"] = from_union([lambda x: from_list(from_str, x), from_none], self.project_paths) + if self.skill_directories is not None: + result["skillDirectories"] = from_union([lambda x: from_list(from_str, x), from_none], self.skill_directories) return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -16756,6 +18752,28 @@ class SlashCommandSetPlanModelResultKind(Enum): class SlashCommandShowDialogResultKind(Enum): SHOW_DIALOG = "show-dialog" +# Experimental: this type is part of an experimental API and may change or be removed. +class StaffLogLevelMigrationStatus(Enum): + """What the one-time staff log-level migration did. + + What the migration did. Only `refreshed` can have changed the persisted settings. + """ + CURRENT = "current" + INELIGIBLE = "ineligible" + REFRESHED = "refreshed" + +# Experimental: this type is part of an experimental API and may change or be removed. +class StaffUpdateChannelMigrationStatus(Enum): + """What the one-time staff update-channel migration did. + + What the migration did. Only `migrated` means the persisted settings changed. + """ + COMPLETED = "completed" + CURRENT = "current" + EXCLUDED_EXPERIMENTAL = "excluded-experimental" + INELIGIBLE = "ineligible" + MIGRATED = "migrated" + # Experimental: this type is part of an experimental API and may change or be removed. class SubagentSettingsEntryContextTier(Enum): """Context tier override for matching subagents""" @@ -17932,6 +19950,828 @@ def to_dict(self) -> dict: result["value"] = self.value return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsApplyTopKeyRequest: + """A single top-level setting subtree to validate and write.""" + + canonical_path: list[str] + """Canonical path of the setting the caller is editing. Currently informational only.""" + + top_key: str + """Top-level setting name to write.""" + + complete_staff_update_channel_migration: bool | None = None + """Records that the user chose an update channel explicitly, so the one-time staff channel + migration stops reapplying. Honored only for the `autoUpdatesChannel` key. + """ + delete: bool | None = None + """True to remove the top-level key instead of writing it.""" + + value: Any = UNSET_SENTINEL + """Value to write for the whole top-level key. Omitted when `delete` is true. Explicit null + is preserved and validated as null. + """ + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsApplyTopKeyRequest': + assert isinstance(obj, dict) + canonical_path = from_list(from_str, obj.get("canonicalPath")) + top_key = from_str(obj.get("topKey")) + complete_staff_update_channel_migration = from_union([from_bool, from_none], obj.get("completeStaffUpdateChannelMigration")) + delete = from_union([from_bool, from_none], obj.get("delete")) + value = obj["value"] if "value" in obj else UNSET_SENTINEL + return UserSettingsApplyTopKeyRequest(canonical_path, top_key, complete_staff_update_channel_migration, delete, value) + + def to_dict(self) -> dict: + result: dict = {} + result["canonicalPath"] = from_list(from_str, self.canonical_path) + result["topKey"] = from_str(self.top_key) + if self.complete_staff_update_channel_migration is not None: + result["completeStaffUpdateChannelMigration"] = from_union([from_bool, from_none], self.complete_staff_update_channel_migration) + if self.delete is not None: + result["delete"] = from_union([from_bool, from_none], self.delete) + if self.value is not UNSET_SENTINEL: + result["value"] = self.value + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsValidationIssue: + """One problem found while validating a setting value.""" + + message: str + """Human-readable description of the problem.""" + + path: list[Any] + """Where in the settings tree the problem sits, as object keys and array indices. The first + element is the top-level key; a whole-value problem is reported as that single key. + """ + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsValidationIssue': + assert isinstance(obj, dict) + message = from_str(obj.get("message")) + path = from_list(lambda x: x, obj.get("path")) + return UserSettingsValidationIssue(message, path) + + def to_dict(self) -> dict: + result: dict = {} + result["message"] = from_str(self.message) + result["path"] = from_list(lambda x: x, self.path) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _UserSettingsBuildRuntimeRequest: + """The overrides and host context to resolve effective runtime settings from.""" + + environment: dict[str, str] + """The environment to read settings-bearing variables from. Passed explicitly rather than + read from the server process so a host can resolve settings for an environment it is + about to create rather than the one it runs in. + """ + platform: str + """The platform to apply platform-specific defaults for, in the same spelling the host's own + runtime reports. + """ + working_directory: str + """The directory the settings are being resolved for. This selects the repository-scoped + document, so a host resolving settings for a directory other than its own process working + directory must pass that directory here. + """ + overrides: dict[str, Any] | None = None + """Settings that take precedence over every document and environment layer, such as the + values a host derived from its own command line. Keyed by setting name, with each value + carried verbatim, so a host may pass keys this runtime does not recognize. Absent when + the host contributes no overrides of its own. + """ + + @staticmethod + def from_dict(obj: Any) -> '_UserSettingsBuildRuntimeRequest': + assert isinstance(obj, dict) + environment = from_dict(from_str, obj.get("environment")) + platform = from_str(obj.get("platform")) + working_directory = from_str(obj.get("workingDirectory")) + overrides = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("overrides")) + return _UserSettingsBuildRuntimeRequest(environment, platform, working_directory, overrides) + + def to_dict(self) -> dict: + result: dict = {} + result["environment"] = from_dict(from_str, self.environment) + result["platform"] = from_str(self.platform) + result["workingDirectory"] = from_str(self.working_directory) + if self.overrides is not None: + result["overrides"] = from_union([lambda x: from_dict(lambda x: x, x), from_none], self.overrides) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsGetPathsRequest: + """Selects which settings file paths to resolve.""" + + repo_root: str | None = None + """Absolute path of a repository root. Supply it to also resolve that repository's two + settings file paths; omit it to resolve only the user path. + """ + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsGetPathsRequest': + assert isinstance(obj, dict) + repo_root = from_union([from_str, from_none], obj.get("repoRoot")) + return UserSettingsGetPathsRequest(repo_root) + + def to_dict(self) -> dict: + result: dict = {} + if self.repo_root is not None: + result["repoRoot"] = from_union([from_str, from_none], self.repo_root) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsGetPathsResult: + """On-disk locations of the settings files this runtime reads. A path is reported whether or + not the file exists. + """ + user_settings: str + """The user settings file: `settings.json` under the resolved Copilot home. This is the file + `user.settings.get`, `user.settings.load`, and `user.settings.set` read and write. + """ + repo_local_settings: str | None = None + """The repository's local, untracked settings file, or null when no repository root was + supplied. + """ + repo_settings: str | None = None + """The repository's checked-in settings file, or null when no repository root was supplied.""" + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsGetPathsResult': + assert isinstance(obj, dict) + user_settings = from_str(obj.get("userSettings")) + repo_local_settings = from_union([from_none, from_str], obj.get("repoLocalSettings")) + repo_settings = from_union([from_none, from_str], obj.get("repoSettings")) + return UserSettingsGetPathsResult(user_settings, repo_local_settings, repo_settings) + + def to_dict(self) -> dict: + result: dict = {} + result["userSettings"] = from_str(self.user_settings) + if self.repo_local_settings is not None: + result["repoLocalSettings"] = from_union([from_none, from_str], self.repo_local_settings) + if self.repo_settings is not None: + result["repoSettings"] = from_union([from_none, from_str], self.repo_settings) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsGovernanceKeysResult: + """Top-level user-setting keys grouped by who may govern them.""" + + claude_repo: list[str] + """Top-level keys a repository may override through a Claude-compatible repository config.""" + + managed: list[str] + """Top-level keys an enterprise policy may fix through managed settings.""" + + repo: list[str] + """Top-level keys a repository may override through `.github/copilot/settings.json` or + `.github/copilot/settings.local.json`. + """ + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsGovernanceKeysResult': + assert isinstance(obj, dict) + claude_repo = from_list(from_str, obj.get("claudeRepo")) + managed = from_list(from_str, obj.get("managed")) + repo = from_list(from_str, obj.get("repo")) + return UserSettingsGovernanceKeysResult(claude_repo, managed, repo) + + def to_dict(self) -> dict: + result: dict = {} + result["claudeRepo"] = from_list(from_str, self.claude_repo) + result["managed"] = from_list(from_str, self.managed) + result["repo"] = from_list(from_str, self.repo) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsHostContext: + """The filesystem and environment facts a user-settings file is resolved from. Passed + explicitly rather than read from the runtime's own process, so a host whose configuration + directory differs from its process environment resolves the file it means. + + The configuration directory, home directory and environment to resolve the settings and + global-state documents from. A host that scopes a session to its own Copilot home must + pass that directory here, or the migration is evaluated against the server's own home + instead. + + The configuration directory, home directory and environment to resolve the settings and + global-state documents from. A host that scopes a session to its own Copilot home must + pass that directory here, or the reset is evaluated against the server's own home + instead. + + The configuration directory, home directory and environment to resolve the settings + document from. A host that scopes a session to its own Copilot home must pass that + directory here, or the server's own home is inspected instead. + """ + environment: dict[str, Any] + """The host's environment variables. `COPILOT_HOME` is read from here, as are the variables + a settings hook may expand. + """ + home_directory: str + """The user's home directory, used when neither a configuration directory nor `COPILOT_HOME` + is set. + """ + config_dir: str | None = None + """The Copilot configuration directory, when the host set one. Takes precedence over + `COPILOT_HOME` and the home directory. + """ + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsHostContext': + assert isinstance(obj, dict) + environment = from_dict(lambda x: x, obj.get("environment")) + home_directory = from_str(obj.get("homeDirectory")) + config_dir = from_union([from_str, from_none], obj.get("configDir")) + return UserSettingsHostContext(environment, home_directory, config_dir) + + def to_dict(self) -> dict: + result: dict = {} + result["environment"] = from_dict(lambda x: x, self.environment) + result["homeDirectory"] = from_str(self.home_directory) + if self.config_dir is not None: + result["configDir"] = from_union([from_str, from_none], self.config_dir) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _UserSettingsLoadAndMergeRepoRequest: + """A repository to read settings from, and the user layer they are merged over.""" + + repo_root: str + """Absolute path of the repository root whose settings to read. When this is the user's home + directory, the Claude-compatible configuration is skipped, so a home-directory session + does not inherit a personal config as if it were repository policy. + """ + user_settings: dict[str, Any] + """The user's own settings, keyed by setting name. The repository layer is merged over this; + a repository may only add to the collection settings it is allowed to influence, so a + user setting it does not name survives unchanged. + """ + + @staticmethod + def from_dict(obj: Any) -> '_UserSettingsLoadAndMergeRepoRequest': + assert isinstance(obj, dict) + repo_root = from_str(obj.get("repoRoot")) + user_settings = from_dict(lambda x: x, obj.get("userSettings")) + return _UserSettingsLoadAndMergeRepoRequest(repo_root, user_settings) + + def to_dict(self) -> dict: + result: dict = {} + result["repoRoot"] = from_str(self.repo_root) + result["userSettings"] = from_dict(lambda x: x, self.user_settings) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _UserSettingsLoadAndMergeRepoResult: + """The settings a consumer should act on inside a repository, together with any diagnostic + raised while reading the repository layer. + """ + settings: dict[str, Any] + """The merged settings document, keyed by setting name. Equals the supplied user layer when + the repository declares no settings. + """ + warning: str | None = None + """Why part of the repository document was ignored, as a single human-readable string + joining every diagnostic with `; `. Null when it loaded cleanly. The settings that did + load are still merged, so a consumer should surface this and continue rather than treat + it as a failure. + """ + + @staticmethod + def from_dict(obj: Any) -> '_UserSettingsLoadAndMergeRepoResult': + assert isinstance(obj, dict) + settings = from_dict(lambda x: x, obj.get("settings")) + warning = from_union([from_none, from_str], obj.get("warning")) + return _UserSettingsLoadAndMergeRepoResult(settings, warning) + + def to_dict(self) -> dict: + result: dict = {} + result["settings"] = from_dict(lambda x: x, self.settings) + if self.warning is not None: + result["warning"] = from_union([from_none, from_str], self.warning) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _UserSettingsLoadForConfigDirRequest: + """Selects the configuration directory whose user settings to read.""" + + config_dir: str | None = None + """Copilot configuration directory to read the settings document from, taking precedence + over the server's own `COPILOT_HOME` and default home. Omit it, or pass an empty string, + to read the directory the server resolved for itself. + """ + + @staticmethod + def from_dict(obj: Any) -> '_UserSettingsLoadForConfigDirRequest': + assert isinstance(obj, dict) + config_dir = from_union([from_str, from_none], obj.get("configDir")) + return _UserSettingsLoadForConfigDirRequest(config_dir) + + def to_dict(self) -> dict: + result: dict = {} + if self.config_dir is not None: + result["configDir"] = from_union([from_str, from_none], self.config_dir) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsLoadForEditResult: + """Contents of settings.json as stored, for a caller about to rewrite it.""" + + status: str + """`ok` when the file parsed, `missing` when there is no file yet, `invalid` when the file + exists but will not parse. + """ + settings: Any = None + """Parsed contents, present only when `status` is `ok`.""" + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsLoadForEditResult': + assert isinstance(obj, dict) + status = from_str(obj.get("status")) + settings = obj.get("settings") + return UserSettingsLoadForEditResult(status, settings) + + def to_dict(self) -> dict: + result: dict = {} + result["status"] = from_str(self.status) + if self.settings is not None: + result["settings"] = self.settings + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsLoadRepoRequest: + """Selects the repository whose checked-in settings to read.""" + + repo_root: str + """Absolute path of the repository root whose settings to read. When this is the user's home + directory, the Claude-compatible configuration is skipped, so a home-directory session + does not inherit a personal config as if it were repository policy. + """ + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsLoadRepoRequest': + assert isinstance(obj, dict) + repo_root = from_str(obj.get("repoRoot")) + return UserSettingsLoadRepoRequest(repo_root) + + def to_dict(self) -> dict: + result: dict = {} + result["repoRoot"] = from_str(self.repo_root) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsLoadRepoResult: + """A repository's settings as stored, together with any diagnostic raised while reading them.""" + + settings: dict[str, Any] | None = None + """The repository's settings document, keyed by setting name, with each value carried + verbatim. Null when the repository declares no settings. + """ + warning: str | None = None + """Why part of the document was ignored, as a single human-readable string joining every + diagnostic with `; `. Null when the document loaded cleanly. The settings that did load + are still reported, so a consumer should surface this and continue rather than treat it + as a failure. + """ + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsLoadRepoResult': + assert isinstance(obj, dict) + settings = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("settings")) + warning = from_union([from_none, from_str], obj.get("warning")) + return UserSettingsLoadRepoResult(settings, warning) + + def to_dict(self) -> dict: + result: dict = {} + if self.settings is not None: + result["settings"] = from_union([lambda x: from_dict(lambda x: x, x), from_none], self.settings) + if self.warning is not None: + result["warning"] = from_union([from_none, from_str], self.warning) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsLoadWithWarningResult: + """The user's stored settings, together with any diagnostic raised while reading them.""" + + settings: dict[str, Any] + """The stored user settings document, keyed by setting name, with each value carried + verbatim. Empty when the file could not be read at all. + """ + warning: str | None = None + """Why part of the document was ignored, phrased for display to the user. Null when the + document loaded cleanly. The settings that did load are still reported, so a consumer may + surface this and continue rather than treat it as a failure. + """ + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsLoadWithWarningResult': + assert isinstance(obj, dict) + settings = from_dict(lambda x: x, obj.get("settings")) + warning = from_union([from_none, from_str], obj.get("warning")) + return UserSettingsLoadWithWarningResult(settings, warning) + + def to_dict(self) -> dict: + result: dict = {} + result["settings"] = from_dict(lambda x: x, self.settings) + if self.warning is not None: + result["warning"] = from_union([from_none, from_str], self.warning) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _UserSettingsMergeManagedAndUserParams: + """The managed (enterprise policy) layer and the user layer to fold together. The field + names carry the layer identity: nothing about a settings object identifies which layer + produced it, so a generic base/override pair would let a caller transpose them and + silently resolve the governance keys in the user's favour. + """ + managed: Any = None + """The managed layer. Its governance keys win, and the per-entry `enabledPlugins` and + `extraKnownMarketplaces` control maps are stamped back from it after the union. Omitted + when the host is unmanaged. + """ + user: Any = None + """The user's own settings layer. Wins on every key the managed layer does not govern. + Omitted when the user has no settings yet. + """ + + @staticmethod + def from_dict(obj: Any) -> '_UserSettingsMergeManagedAndUserParams': + assert isinstance(obj, dict) + managed = obj.get("managed") + user = obj.get("user") + return _UserSettingsMergeManagedAndUserParams(managed, user) + + def to_dict(self) -> dict: + result: dict = {} + if self.managed is not None: + result["managed"] = self.managed + if self.user is not None: + result["user"] = self.user + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsMergeManagedAndUserResult: + """The user layer folded over the managed layer.""" + + settings_json: Any + """The merged settings. An empty object when neither layer was supplied, so an unmanaged + host with no user settings governs nothing. + """ + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsMergeManagedAndUserResult': + assert isinstance(obj, dict) + settings_json = obj.get("settingsJson") + return UserSettingsMergeManagedAndUserResult(settings_json) + + def to_dict(self) -> dict: + result: dict = {} + result["settingsJson"] = self.settings_json + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _UserSettingsMergeRepoValueParams: + """The user layer and the repository layer to fold together.""" + + repo_settings: Any + """The repository's checked-in settings layer.""" + + user_settings: Any + """The user's settings, already resolved against any managed layer.""" + + @staticmethod + def from_dict(obj: Any) -> '_UserSettingsMergeRepoValueParams': + assert isinstance(obj, dict) + repo_settings = obj.get("repoSettings") + user_settings = obj.get("userSettings") + return _UserSettingsMergeRepoValueParams(repo_settings, user_settings) + + def to_dict(self) -> dict: + result: dict = {} + result["repoSettings"] = self.repo_settings + result["userSettings"] = self.user_settings + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsMergeRepoValueResult: + """The repository layer folded into the user layer.""" + + settings_json: Any + """The merged settings, with the per-entry managed control maps re-stamped from the + runtime's ambient managed policy. + """ + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsMergeRepoValueResult': + assert isinstance(obj, dict) + settings_json = obj.get("settingsJson") + return UserSettingsMergeRepoValueResult(settings_json) + + def to_dict(self) -> dict: + result: dict = {} + result["settingsJson"] = self.settings_json + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _UserSettingsMigrateObsoleteOnDiskRequest: + """Selects the configuration directory whose settings document to clean.""" + + config_dir: str | None = None + """The configuration directory holding the settings document to clean. A host that scopes a + session to its own Copilot home passes that directory here; omit it to clean the document + under the home the server resolved for itself. The home directory and environment are + deliberately not caller-supplied: they decide only the settings path root and the two + hook-parsing flags, and those describe the process doing the reading rather than the + caller. + """ + + @staticmethod + def from_dict(obj: Any) -> '_UserSettingsMigrateObsoleteOnDiskRequest': + assert isinstance(obj, dict) + config_dir = from_union([from_str, from_none], obj.get("configDir")) + return _UserSettingsMigrateObsoleteOnDiskRequest(config_dir) + + def to_dict(self) -> dict: + result: dict = {} + if self.config_dir is not None: + result["configDir"] = from_union([from_str, from_none], self.config_dir) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +class _UserSettingsMigrateObsoleteOnDiskStatus(Enum): + """What the cleanup did. Only `changed` rewrote the document. The cleaned settings are not + returned here: the document is written before this replies, so `user.settings.load` reads + it authoritatively. + + What the one-time obsolete-settings cleanup did to the document. + """ + CHANGED = "changed" + INVALID = "invalid" + MISSING = "missing" + UNCHANGED = "unchanged" + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsNormalizeRequest: + """Settings object whose key names should be canonicalized.""" + + settings: Any + """Free-form settings object, possibly using legacy or snake_case key names.""" + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsNormalizeRequest': + assert isinstance(obj, dict) + settings = obj.get("settings") + return UserSettingsNormalizeRequest(settings) + + def to_dict(self) -> dict: + result: dict = {} + result["settings"] = self.settings + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsNormalizeResult: + """Settings object with canonical key names.""" + + settings: Any + """The same settings with every legacy or snake_case key renamed to its canonical spelling.""" + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsNormalizeResult': + assert isinstance(obj, dict) + settings = obj.get("settings") + return UserSettingsNormalizeResult(settings) + + def to_dict(self) -> dict: + result: dict = {} + result["settings"] = self.settings + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsPathResult: + """The on-disk location of the user's settings document.""" + + path: str + """Absolute path to the settings file this runtime reads and writes. Normally + `settings.json` under the Copilot configuration directory; an extensionless legacy + `settings` file is reported instead when it exists and `settings.json` does not. + """ + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsPathResult': + assert isinstance(obj, dict) + path = from_str(obj.get("path")) + return UserSettingsPathResult(path) + + def to_dict(self) -> dict: + result: dict = {} + result["path"] = from_str(self.path) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsProblemLocation: + """The settings layer the problem was found in. + + The settings layer a problem was found in. + """ + file_path: str + """Absolute path of the file backing this layer.""" + + kind: str + """Which layer this is: `user`, `repo`, or `local`.""" + + label: str + """The caller-supplied display label for the layer.""" + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsProblemLocation': + assert isinstance(obj, dict) + file_path = from_str(obj.get("filePath")) + kind = from_str(obj.get("kind")) + label = from_str(obj.get("label")) + return UserSettingsProblemLocation(file_path, kind, label) + + def to_dict(self) -> dict: + result: dict = {} + result["filePath"] = from_str(self.file_path) + result["kind"] = from_str(self.kind) + result["label"] = from_str(self.label) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsProblemSource: + """One already-loaded settings layer to diagnose.""" + + file_path: str + """Absolute path of the file backing this layer.""" + + kind: str + """Which layer this is: `user`, `repo`, or `local`.""" + + label: str + """Caller-chosen display label for the layer, echoed back on each problem so a caller can + group by layer without re-deriving its own naming. + """ + config: dict[str, Any] | None = None + """The layer's settings exactly as loaded, unnormalized. Empty when the file is missing or + failed to parse. + """ + invalid: bool | None = None + """True when the file itself failed to parse, so its keys could not be read at all.""" + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsProblemSource': + assert isinstance(obj, dict) + file_path = from_str(obj.get("filePath")) + kind = from_str(obj.get("kind")) + label = from_str(obj.get("label")) + config = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("config")) + invalid = from_union([from_bool, from_none], obj.get("invalid")) + return UserSettingsProblemSource(file_path, kind, label, config, invalid) + + def to_dict(self) -> dict: + result: dict = {} + result["filePath"] = from_str(self.file_path) + result["kind"] = from_str(self.kind) + result["label"] = from_str(self.label) + if self.config is not None: + result["config"] = from_union([lambda x: from_dict(lambda x: x, x), from_none], self.config) + if self.invalid is not None: + result["invalid"] = from_union([from_bool, from_none], self.invalid) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _UserSettingsResetStaffModelSelectionOnceResult: + """The outcome of the one-time staff model-selection reset.""" + + cutoff: str + """The cutoff this reset is gated on, as an ISO-8601 timestamp. A recorded timestamp at or + after it means the reset is already done. + """ + did_reset: bool + """Whether the persisted model selection was cleared. Only `true` means the settings + document changed and the host should adopt the returned settings. + """ + reset_at: str | None = None + """The timestamp the host should now record, as an ISO-8601 timestamp. Carries the caller's + own value back unchanged when nothing was reset. + """ + settings: dict[str, Any] | None = None + """The settings document as it stands after the reset, keyed by setting name. Present only + when `didReset` is true. + """ + + @staticmethod + def from_dict(obj: Any) -> '_UserSettingsResetStaffModelSelectionOnceResult': + assert isinstance(obj, dict) + cutoff = from_str(obj.get("cutoff")) + did_reset = from_bool(obj.get("didReset")) + reset_at = from_union([from_str, from_none], obj.get("resetAt")) + settings = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("settings")) + return _UserSettingsResetStaffModelSelectionOnceResult(cutoff, did_reset, reset_at, settings) + + def to_dict(self) -> dict: + result: dict = {} + result["cutoff"] = from_str(self.cutoff) + result["didReset"] = from_bool(self.did_reset) + if self.reset_at is not None: + result["resetAt"] = from_union([from_str, from_none], self.reset_at) + if self.settings is not None: + result["settings"] = from_union([lambda x: from_dict(lambda x: x, x), from_none], self.settings) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _UserSettingsSetHookDisabledRequest: + """One hook to disable or re-enable.""" + + disabled: bool + """True adds the key to `disabledHooks`, false removes it.""" + + disable_key: str + """Durable content-hash key of the hook, as `hooks.loadResources` reports it.""" + + settings_path: str + """Path of the settings.json to rewrite.""" + + @staticmethod + def from_dict(obj: Any) -> '_UserSettingsSetHookDisabledRequest': + assert isinstance(obj, dict) + disabled = from_bool(obj.get("disabled")) + disable_key = from_str(obj.get("disableKey")) + settings_path = from_str(obj.get("settingsPath")) + return _UserSettingsSetHookDisabledRequest(disabled, disable_key, settings_path) + + def to_dict(self) -> dict: + result: dict = {} + result["disabled"] = from_bool(self.disabled) + result["disableKey"] = from_str(self.disable_key) + result["settingsPath"] = from_str(self.settings_path) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _UserSettingsSetHookDisabledResult: + """Outcome of rewriting the `disabledHooks` setting.""" + + status: str + """`ok` when the setting was written, `invalid` when settings.json could not be parsed and + was left alone, `error` when the write itself failed. + """ + error_message: str | None = None + """Why the write failed. Absent when the status is `ok`.""" + + @staticmethod + def from_dict(obj: Any) -> '_UserSettingsSetHookDisabledResult': + assert isinstance(obj, dict) + status = from_str(obj.get("status")) + error_message = from_union([from_str, from_none], obj.get("errorMessage")) + return _UserSettingsSetHookDisabledResult(status, error_message) + + def to_dict(self) -> dict: + result: dict = {} + result["status"] = from_str(self.status) + if self.error_message is not None: + result["errorMessage"] = from_union([from_str, from_none], self.error_message) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class UserSettingsSetRequest: @@ -17952,6 +20792,150 @@ def to_dict(self) -> dict: result["settings"] = self.settings return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _UserSettingsUnknownKeysWarningResult: + """The unrecognized top-level keys found in the user settings document, already phrased for + display. + """ + warning: str | None = None + """The warning to show the user, naming the unrecognized keys and the file they were found + in. Absent when every top-level key is recognized, and also when the document is missing + or could not be read, because an unknown-key list taken from a document the runtime could + not load would name keys that may not be there. + """ + + @staticmethod + def from_dict(obj: Any) -> '_UserSettingsUnknownKeysWarningResult': + assert isinstance(obj, dict) + warning = from_union([from_str, from_none], obj.get("warning")) + return _UserSettingsUnknownKeysWarningResult(warning) + + def to_dict(self) -> dict: + result: dict = {} + if self.warning is not None: + result["warning"] = from_union([from_str, from_none], self.warning) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _UserSettingsUpdateRequest: + """Names the user setting to merge into, and the partial object to merge.""" + + key: str + """Top-level user-setting key to merge into, such as `extensions`.""" + + value: Any + """Value to store for `key`. When both the stored value and this value are objects, their + fields are shallow-merged and fields this object does not name are preserved. Otherwise + this value replaces the stored value. + """ + config_dir: str | None = None + """Copilot configuration directory to write the settings document in, taking precedence over + the server's own `COPILOT_HOME` and default home. Omit it, or pass an empty string, to + use the directory the server resolved for itself. + """ + sub_dir: str | None = None + """Single-component name for the settings document at `/settings/.json`; + path separators, `.` and `..` are rejected. Omit it, or pass an empty string, to use the + main settings document. + """ + + @staticmethod + def from_dict(obj: Any) -> '_UserSettingsUpdateRequest': + assert isinstance(obj, dict) + key = from_str(obj.get("key")) + value = obj.get("value") + config_dir = from_union([from_str, from_none], obj.get("configDir")) + sub_dir = from_union([from_str, from_none], obj.get("subDir")) + return _UserSettingsUpdateRequest(key, value, config_dir, sub_dir) + + def to_dict(self) -> dict: + result: dict = {} + result["key"] = from_str(self.key) + result["value"] = self.value + if self.config_dir is not None: + result["configDir"] = from_union([from_str, from_none], self.config_dir) + if self.sub_dir is not None: + result["subDir"] = from_union([from_str, from_none], self.sub_dir) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _UserSettingsUpdateResult: + """The stored value of the user setting after the merge.""" + + value: Any + """The key's stored value after the write: the merged object when both the stored value and + the supplied value are objects, otherwise the supplied value. + """ + + @staticmethod + def from_dict(obj: Any) -> '_UserSettingsUpdateResult': + assert isinstance(obj, dict) + value = obj.get("value") + return _UserSettingsUpdateResult(value) + + def to_dict(self) -> dict: + result: dict = {} + result["value"] = self.value + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsValidateRequest: + """A single top-level setting subtree to validate.""" + + top_key: str + """Top-level setting name to validate.""" + + delete: bool | None = None + """True to validate a removal of this key. The current settings file is not read.""" + + value: Any = None + """Value to validate. Omitted when `delete` is true.""" + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsValidateRequest': + assert isinstance(obj, dict) + top_key = from_str(obj.get("topKey")) + delete = from_union([from_bool, from_none], obj.get("delete")) + value = obj.get("value") + return UserSettingsValidateRequest(top_key, delete, value) + + def to_dict(self) -> dict: + result: dict = {} + result["topKey"] = from_str(self.top_key) + if self.delete is not None: + result["delete"] = from_union([from_bool, from_none], self.delete) + if self.value is not None: + result["value"] = self.value + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsWriteAllRequest: + """The whole content to store in the user settings file.""" + + settings: Any + """The settings object to store. It replaces the file's entire content, so a key absent here + is removed. + """ + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsWriteAllRequest': + assert isinstance(obj, dict) + settings = obj.get("settings") + return UserSettingsWriteAllRequest(settings) + + def to_dict(self) -> dict: + result: dict = {} + result["settings"] = self.settings + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class WorkflowAbortRequest: @@ -19081,6 +22065,16 @@ def to_dict(self) -> dict: result["operation"] = from_str(self.operation) return result +# Experimental: this type is part of an experimental API and may change or be removed. +class ConnectorDiscoveryAvailability(Enum): + "Availability." + # Enabled. + ENABLED = "enabled" + # Disabled. + DISABLED = "disabled" + # Unavailable. + UNAVAILABLE = "unavailable" + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class AuthIdentityMetadata: @@ -19202,6 +22196,10 @@ class AccountStatus: selection_id: str """Opaque selection id used to switch to, or log out, this account.""" + auth_source: str | None = None + """Human-readable credential source used to authenticate this account, such as `gh` or + `GITHUB_TOKEN`. + """ derived_from: str | None = None """Opaque id of the account this one was derived from (e.g. an EMU account's base Entra identity); absent for a root account. Matches the base identity account's selectionId, @@ -19216,8 +22214,9 @@ def from_dict(obj: Any) -> 'AccountStatus': kind = AccountKind(obj.get("kind")) login = from_str(obj.get("login")) selection_id = from_str(obj.get("selectionId")) + auth_source = from_union([from_str, from_none], obj.get("authSource")) derived_from = from_union([from_str, from_none], obj.get("derivedFrom")) - return AccountStatus(active, host, kind, login, selection_id, derived_from) + return AccountStatus(active, host, kind, login, selection_id, auth_source, derived_from) def to_dict(self) -> dict: result: dict = {} @@ -19226,6 +22225,8 @@ def to_dict(self) -> dict: result["kind"] = to_enum(AccountKind, self.kind) result["login"] = from_str(self.login) result["selectionId"] = from_str(self.selection_id) + if self.auth_source is not None: + result["authSource"] = from_union([from_str, from_none], self.auth_source) if self.derived_from is not None: result["derivedFrom"] = from_union([from_str, from_none], self.derived_from) return result @@ -19246,6 +22247,10 @@ class ItemElement: active: bool | None = None """Whether this is the active account.""" + auth_source: str | None = None + """Human-readable credential source used to authenticate this account, such as `gh` or + `GITHUB_TOKEN`. + """ derived_from: str | None = None """Opaque id of the account this one was derived from (e.g. an EMU account's base Entra identity); absent for a root account. Matches the base identity account's selectionId, @@ -19271,19 +22276,22 @@ def from_dict(obj: Any) -> 'ItemElement': assert isinstance(obj, dict) kind = AccountKind(obj.get("kind")) active = from_union([from_bool, from_none], obj.get("active")) + auth_source = from_union([from_str, from_none], obj.get("authSource")) derived_from = from_union([from_str, from_none], obj.get("derivedFrom")) host = from_union([from_str, from_none], obj.get("host")) login = from_union([from_str, from_none], obj.get("login")) selection_id = from_union([from_str, from_none], obj.get("selectionId")) available = from_union([from_bool, from_none], obj.get("available")) label = from_union([from_str, from_none], obj.get("label")) - return ItemElement(kind, active, derived_from, host, login, selection_id, available, label) + return ItemElement(kind, active, auth_source, derived_from, host, login, selection_id, available, label) def to_dict(self) -> dict: result: dict = {} result["kind"] = to_enum(AccountKind, self.kind) if self.active is not None: result["active"] = from_union([from_bool, from_none], self.active) + if self.auth_source is not None: + result["authSource"] = from_union([from_str, from_none], self.auth_source) if self.derived_from is not None: result["derivedFrom"] = from_union([from_str, from_none], self.derived_from) if self.host is not None: @@ -22006,6 +25014,32 @@ def to_dict(self) -> dict: result["callbackUrl"] = from_str(self.callback_url) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class ModelGetReasoningSummariesRequest: + """Host presentation state the reasoning-summaries policy depends on.""" + + persisted_show_reasoning: bool | None = None + """The host's persisted `showReasoning` user setting, omitted when unset.""" + + summaries_visible: bool | None = None + """Whether the host is currently showing reasoning summaries. Defaults to false.""" + + @staticmethod + def from_dict(obj: Any) -> 'ModelGetReasoningSummariesRequest': + assert isinstance(obj, dict) + persisted_show_reasoning = from_union([from_bool, from_none], obj.get("persistedShowReasoning")) + summaries_visible = from_union([from_bool, from_none], obj.get("summariesVisible")) + return ModelGetReasoningSummariesRequest(persisted_show_reasoning, summaries_visible) + + def to_dict(self) -> dict: + result: dict = {} + if self.persisted_show_reasoning is not None: + result["persistedShowReasoning"] = from_union([from_bool, from_none], self.persisted_show_reasoning) + if self.summaries_visible is not None: + result["summariesVisible"] = from_union([from_bool, from_none], self.summaries_visible) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ModelSetAllowedModelsRequest: @@ -23273,6 +26307,60 @@ def to_dict(self) -> dict: result["type"] = self.type return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _FeatureFlagMetadata: + """The declared shape of one feature flag, independent of any caller population.""" + + availability: _FeatureFlagAvailability + """The population the flag is declared for before any environment or user-settings override + is applied. + """ + capi_sanity: bool + """Whether the flag participates in the CAPI sanity suite.""" + + name: str + """The flag's name, matching the key it is listed under.""" + + experimental_description: str | None = None + """Text shown to a user choosing whether to turn the flag on in experimental mode. Absent + when the flag is not user-facing. + """ + exp_flag: str | None = None + """The online-experiment (ExP) flag this maps to, when the flag is assigned by an experiment + rather than declared statically. + """ + exp_flag_env_var: str | None = None + """Environment variable that overrides this flag's experiment assignment locally in the + experiment service, not in `featureFlags.resolve`. Present exactly when `expFlag` is, and + derived from it, so a caller reads a local override without reproducing the naming rule. + """ + + @staticmethod + def from_dict(obj: Any) -> '_FeatureFlagMetadata': + assert isinstance(obj, dict) + availability = _FeatureFlagAvailability(obj.get("availability")) + capi_sanity = from_bool(obj.get("capiSanity")) + name = from_str(obj.get("name")) + experimental_description = from_union([from_str, from_none], obj.get("experimentalDescription")) + exp_flag = from_union([from_str, from_none], obj.get("expFlag")) + exp_flag_env_var = from_union([from_str, from_none], obj.get("expFlagEnvVar")) + return _FeatureFlagMetadata(availability, capi_sanity, name, experimental_description, exp_flag, exp_flag_env_var) + + def to_dict(self) -> dict: + result: dict = {} + result["availability"] = to_enum(_FeatureFlagAvailability, self.availability) + result["capiSanity"] = from_bool(self.capi_sanity) + result["name"] = from_str(self.name) + if self.experimental_description is not None: + result["experimentalDescription"] = from_union([from_str, from_none], self.experimental_description) + if self.exp_flag is not None: + result["expFlag"] = from_union([from_str, from_none], self.exp_flag) + if self.exp_flag_env_var is not None: + result["expFlagEnvVar"] = from_union([from_str, from_none], self.exp_flag_env_var) + return result + # Experimental: this type is part of an experimental API and may change or be removed. # Internal: this type is an internal SDK API and is not part of the public surface. @dataclass @@ -24908,6 +27996,91 @@ def to_dict(self) -> dict: result["url"] = from_union([from_str, from_none], self.url) return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _ManagedSettingsResolveBypassDisableTransitionResult: + """The bypass-permissions state a host should hold after the resolution, and what it must do + to get there. + """ + suppress_runtime_allow_all: bool + """Whether a bypass-permissions session already running must be turned off. True only when + this resolution newly disables bypass mode, so a host that is already compliant is not + interrupted. + """ + managed_identity: str | None = None + """The account the returned `managed` source is pinned to. Store it alongside `source`; the + next call needs it to decide whether an account switch lifts the disable. + """ + set_disabled_state: bool | None = None + """The value to store in the host's own bypass-disabled flag. Omitted means leave it exactly + as it is, which is not the same as being told to store `false`. + """ + source: _ManagedSettingsBypassDisableSource | None = None + """Which layer disables bypass mode after this resolution. Omitted when none does, which is + what a lifted disable looks like. + """ + + @staticmethod + def from_dict(obj: Any) -> '_ManagedSettingsResolveBypassDisableTransitionResult': + assert isinstance(obj, dict) + suppress_runtime_allow_all = from_bool(obj.get("suppressRuntimeAllowAll")) + managed_identity = from_union([from_str, from_none], obj.get("managedIdentity")) + set_disabled_state = from_union([from_bool, from_none], obj.get("setDisabledState")) + source = from_union([_ManagedSettingsBypassDisableSource, from_none], obj.get("source")) + return _ManagedSettingsResolveBypassDisableTransitionResult(suppress_runtime_allow_all, managed_identity, set_disabled_state, source) + + def to_dict(self) -> dict: + result: dict = {} + result["suppressRuntimeAllowAll"] = from_bool(self.suppress_runtime_allow_all) + if self.managed_identity is not None: + result["managedIdentity"] = from_union([from_str, from_none], self.managed_identity) + if self.set_disabled_state is not None: + result["setDisabledState"] = from_union([from_bool, from_none], self.set_disabled_state) + if self.source is not None: + result["source"] = from_union([lambda x: to_enum(_ManagedSettingsBypassDisableSource, x), from_none], self.source) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _ManagedSettingsResolveBypassDisableTransitionParams: + """The bypass-permissions state a host holds today, the account now signed in, and what the + managed-policy resolution said. + """ + active_identity: str + """The account signed in now. A `managed` disable pinned to a different account is lifted.""" + + policy: _ManagedSettingsBypassPolicyResolution + """What the resolution said about bypass mode. Send `unknown` rather than omitting the field + when there is no verdict. + """ + current_managed_identity: str | None = None + """The account a `managed` disable is pinned to. Omit it when the current source is not + `managed`; it is ignored for the other sources. + """ + current_source: _ManagedSettingsBypassDisableSource | None = None + """Which layer currently disables bypass mode. Omit it when nothing does.""" + + @staticmethod + def from_dict(obj: Any) -> '_ManagedSettingsResolveBypassDisableTransitionParams': + assert isinstance(obj, dict) + active_identity = from_str(obj.get("activeIdentity")) + policy = _ManagedSettingsBypassPolicyResolution(obj.get("policy")) + current_managed_identity = from_union([from_str, from_none], obj.get("currentManagedIdentity")) + current_source = from_union([_ManagedSettingsBypassDisableSource, from_none], obj.get("currentSource")) + return _ManagedSettingsResolveBypassDisableTransitionParams(active_identity, policy, current_managed_identity, current_source) + + def to_dict(self) -> dict: + result: dict = {} + result["activeIdentity"] = from_str(self.active_identity) + result["policy"] = to_enum(_ManagedSettingsBypassPolicyResolution, self.policy) + if self.current_managed_identity is not None: + result["currentManagedIdentity"] = from_union([from_str, from_none], self.current_managed_identity) + if self.current_source is not None: + result["currentSource"] = from_union([lambda x: to_enum(_ManagedSettingsBypassDisableSource, x), from_none], self.current_source) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ManagedSettingsComposeLayer: @@ -25009,6 +28182,500 @@ def to_dict(self) -> dict: result["model"] = from_union([lambda x: to_class(ManagedSettingMeta, x), from_none], self.model) return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _ManagedSettingsLoadRemoteControlParams: + """Inputs describing the host and account whose managed remote-control policy should be + resolved. Mirrors `managedSettings.ingest`, and deliberately carries no retention + decision: reading this gate never becomes this runtime process's startup policy. + """ + environment: Any + """The host's environment variables, read for cache location and policy overrides.""" + + home_directory: str + """The host's home directory, used to resolve cache and managed-file locations.""" + + platform: str + """The host's platform, in Node's `process.platform` spelling, used to locate + platform-specific cache and policy paths. + """ + user_agent: str + """User agent to send with the policy fetch.""" + + auth_info: Any = None + """The authenticated identity to resolve policy for. Omitted before sign-in, in which case + no server layer is fetched. + """ + client_name: str | None = None + """Name of the embedding client, reported to the managed-settings service as `client_id` so + it can refuse to deliver a policy this client is too old to enforce. That refusal is + exactly what turns into an explicit `disabled` below, so a host that omits this field + forfeits the protection. + """ + device_response: Any = None + """A device layer supplied by the host instead of discovered from the machine. Pair it with + `hasDeviceResponse` so an explicit absence is distinguishable from an omitted override. + """ + has_device_response: bool | None = None + """Whether `deviceResponse` is an override at all. True with a null `deviceResponse` asserts + the device layer is empty; false discovers it from the machine. + """ + policy_helper_enabled: bool | None = None + """Whether a configured platform policy helper may be run to produce an enforced helper + layer. + """ + policy_helper_reason: _ManagedSettingsPolicyHelperReason | None = None + """The policy-helper invocation reason. Defaults to sessionStart.""" + + self_fetch: bool | None = None + """Whether to run ingestion. Omit or set false to return an empty result without discovering + or fetching policy. + """ + + @staticmethod + def from_dict(obj: Any) -> '_ManagedSettingsLoadRemoteControlParams': + assert isinstance(obj, dict) + environment = obj.get("environment") + home_directory = from_str(obj.get("homeDirectory")) + platform = from_str(obj.get("platform")) + user_agent = from_str(obj.get("userAgent")) + auth_info = obj.get("authInfo") + client_name = from_union([from_str, from_none], obj.get("clientName")) + device_response = obj.get("deviceResponse") + has_device_response = from_union([from_bool, from_none], obj.get("hasDeviceResponse")) + policy_helper_enabled = from_union([from_bool, from_none], obj.get("policyHelperEnabled")) + policy_helper_reason = from_union([_ManagedSettingsPolicyHelperReason, from_none], obj.get("policyHelperReason")) + self_fetch = from_union([from_bool, from_none], obj.get("selfFetch")) + return _ManagedSettingsLoadRemoteControlParams(environment, home_directory, platform, user_agent, auth_info, client_name, device_response, has_device_response, policy_helper_enabled, policy_helper_reason, self_fetch) + + def to_dict(self) -> dict: + result: dict = {} + result["environment"] = self.environment + result["homeDirectory"] = from_str(self.home_directory) + result["platform"] = from_str(self.platform) + result["userAgent"] = from_str(self.user_agent) + if self.auth_info is not None: + result["authInfo"] = self.auth_info + if self.client_name is not None: + result["clientName"] = from_union([from_str, from_none], self.client_name) + if self.device_response is not None: + result["deviceResponse"] = self.device_response + if self.has_device_response is not None: + result["hasDeviceResponse"] = from_union([from_bool, from_none], self.has_device_response) + if self.policy_helper_enabled is not None: + result["policyHelperEnabled"] = from_union([from_bool, from_none], self.policy_helper_enabled) + if self.policy_helper_reason is not None: + result["policyHelperReason"] = from_union([lambda x: to_enum(_ManagedSettingsPolicyHelperReason, x), from_none], self.policy_helper_reason) + if self.self_fetch is not None: + result["selfFetch"] = from_union([from_bool, from_none], self.self_fetch) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _ManagedSettingsIngestParams: + """Inputs describing the host and account an ingestion should resolve managed policy for.""" + + environment: Any + """The host's environment variables, read for cache location and policy overrides. Supplied + by the host rather than read from the runtime process so a command that selected its own + configuration home is honored. + """ + home_directory: str + """The host's home directory, used to resolve cache and managed-file locations.""" + + platform: str + """The host's platform, in Node's `process.platform` spelling, used to locate + platform-specific cache and policy paths. + """ + user_agent: str + """User agent to send with the policy fetch.""" + + auth_info: Any = None + """The authenticated identity to resolve policy for. Omitted before sign-in, in which case + no server layer is fetched. + """ + bypass_cache_read: bool | None = None + """Whether to ignore a cached server policy when reading, forcing the resolution to come + from a live fetch. + """ + client_name: str | None = None + """Name of the embedding client, reported to the managed-settings service as `client_id` so + it can refuse to deliver a policy this client is too old to enforce. Omit it and the + runtime reports a generic identity; the runtime version sent alongside is the gate that + actually matters. + """ + device_response: Any = None + """A device layer supplied by the host instead of discovered from the machine. Pair it with + `hasDeviceResponse` so an explicit absence is distinguishable from an omitted override. + """ + has_device_response: bool | None = None + """Whether `deviceResponse` is an override at all. True with a null `deviceResponse` asserts + the device layer is empty; false discovers it from the machine. + """ + policy_helper_enabled: bool | None = None + """Whether a configured platform policy helper may be run to produce an enforced helper + layer. + """ + policy_helper_reason: _ManagedSettingsPolicyHelperReason | None = None + """The policy-helper invocation reason. Defaults to sessionStart.""" + + policy_helper_working_directory: str | None = None + """Directory the policy helper runs in when this ingestion is not owned by a registered + session. A host resolving policy before its session exists must pass this, or a + directory-sensitive helper judges the wrong repository. + """ + self_fetch: bool | None = None + """Whether to run ingestion. Omit or set false to return an empty result without discovering + or fetching policy. + """ + server_policy_cache_mode: _ManagedSettingsServerPolicyCacheMode | None = None + """How this ingestion may use the on-disk server-policy cache. Defaults to the runtime's own + policy when omitted. + """ + session_id: str | None = None + """The session whose credential store and working directory the policy helper should use. + Omit it when no session exists yet and the process-wide store applies. + """ + + @staticmethod + def from_dict(obj: Any) -> '_ManagedSettingsIngestParams': + assert isinstance(obj, dict) + environment = obj.get("environment") + home_directory = from_str(obj.get("homeDirectory")) + platform = from_str(obj.get("platform")) + user_agent = from_str(obj.get("userAgent")) + auth_info = obj.get("authInfo") + bypass_cache_read = from_union([from_bool, from_none], obj.get("bypassCacheRead")) + client_name = from_union([from_str, from_none], obj.get("clientName")) + device_response = obj.get("deviceResponse") + has_device_response = from_union([from_bool, from_none], obj.get("hasDeviceResponse")) + policy_helper_enabled = from_union([from_bool, from_none], obj.get("policyHelperEnabled")) + policy_helper_reason = from_union([_ManagedSettingsPolicyHelperReason, from_none], obj.get("policyHelperReason")) + policy_helper_working_directory = from_union([from_str, from_none], obj.get("policyHelperWorkingDirectory")) + self_fetch = from_union([from_bool, from_none], obj.get("selfFetch")) + server_policy_cache_mode = from_union([_ManagedSettingsServerPolicyCacheMode, from_none], obj.get("serverPolicyCacheMode")) + session_id = from_union([from_str, from_none], obj.get("sessionId")) + return _ManagedSettingsIngestParams(environment, home_directory, platform, user_agent, auth_info, bypass_cache_read, client_name, device_response, has_device_response, policy_helper_enabled, policy_helper_reason, policy_helper_working_directory, self_fetch, server_policy_cache_mode, session_id) + + def to_dict(self) -> dict: + result: dict = {} + result["environment"] = self.environment + result["homeDirectory"] = from_str(self.home_directory) + result["platform"] = from_str(self.platform) + result["userAgent"] = from_str(self.user_agent) + if self.auth_info is not None: + result["authInfo"] = self.auth_info + if self.bypass_cache_read is not None: + result["bypassCacheRead"] = from_union([from_bool, from_none], self.bypass_cache_read) + if self.client_name is not None: + result["clientName"] = from_union([from_str, from_none], self.client_name) + if self.device_response is not None: + result["deviceResponse"] = self.device_response + if self.has_device_response is not None: + result["hasDeviceResponse"] = from_union([from_bool, from_none], self.has_device_response) + if self.policy_helper_enabled is not None: + result["policyHelperEnabled"] = from_union([from_bool, from_none], self.policy_helper_enabled) + if self.policy_helper_reason is not None: + result["policyHelperReason"] = from_union([lambda x: to_enum(_ManagedSettingsPolicyHelperReason, x), from_none], self.policy_helper_reason) + if self.policy_helper_working_directory is not None: + result["policyHelperWorkingDirectory"] = from_union([from_str, from_none], self.policy_helper_working_directory) + if self.self_fetch is not None: + result["selfFetch"] = from_union([from_bool, from_none], self.self_fetch) + if self.server_policy_cache_mode is not None: + result["serverPolicyCacheMode"] = from_union([lambda x: to_enum(_ManagedSettingsServerPolicyCacheMode, x), from_none], self.server_policy_cache_mode) + if self.session_id is not None: + result["sessionId"] = from_union([from_str, from_none], self.session_id) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _ManagedSettingsResolveStartupPolicyParams: + """Inputs describing the host and account a startup policy resolution should run for. + Mirrors `managedSettings.ingest`, and adds the decision of whether the resolved policy is + retained for this runtime process. + """ + environment: Any + """The host's environment variables, read for cache location, policy overrides, and the + variables that would make a membership snapshot forgeable. + """ + home_directory: str + """The host's home directory, used to resolve cache and managed-file locations.""" + + platform: str + """The host's platform, in Node's `process.platform` spelling, used to locate + platform-specific cache and policy paths. + """ + retain_and_apply: bool + """Whether to retain the resolved policy as this runtime process's startup policy, so later + work in the process enforces it. Set false to resolve without side effects, which is what + a protocol host wants before session creation: its sessions apply the complete policy + through the normal lifecycle, and retaining here would let a readiness credential affect + unrelated sessions. The returned verdict is identical either way — this decides only + whether the process keeps it. + """ + user_agent: str + """User agent to send with the policy fetch.""" + + auth_info: Any = None + """The authenticated identity to resolve policy for. Omitted before sign-in, in which case + no server layer is fetched and the sign-in pin is evaluated against no credential. + """ + bypass_cache_read: bool | None = None + """Whether to ignore a cached server policy when reading, forcing the resolution to come + from a live fetch. + """ + client_name: str | None = None + """Name of the embedding client, reported to the managed-settings service as `client_id` so + it can refuse to deliver a policy this client is too old to enforce. + """ + device_response: Any = None + """A device layer supplied by the host instead of discovered from the machine. Pair it with + `hasDeviceResponse` so an explicit absence is distinguishable from an omitted override. + """ + has_device_response: bool | None = None + """Whether `deviceResponse` is an override at all. True with a null `deviceResponse` asserts + the device layer is empty; false discovers it from the machine. + """ + policy_helper_enabled: bool | None = None + """Whether a configured platform policy helper may be run to produce an enforced helper + layer. + """ + policy_helper_reason: _ManagedSettingsPolicyHelperReason | None = None + """The policy-helper invocation reason. Defaults to sessionStart.""" + + policy_helper_working_directory: str | None = None + """Directory the policy helper runs in when this resolution is not owned by a registered + session. A host resolving policy before its session exists must pass this, or a + directory-sensitive helper judges the wrong repository. + """ + self_fetch: bool | None = None + """Whether to run ingestion. Omit or set false to return an empty result without discovering + or fetching policy. + """ + server_policy_cache_mode: _ManagedSettingsServerPolicyCacheMode | None = None + """How this resolution may use the on-disk server-policy cache. Defaults to the runtime's + own policy when omitted. Rejected when combined with `bypassCacheRead`, because the two + express the same decision and accepting both would leave the caller unsure which one the + runtime honored. + """ + session_id: str | None = None + """The session whose credential store, working directory and client kind the resolution + should use. Omit it when no session exists yet and the process-wide store applies. A + resolution whose session names a credential other than `authInfo` is returned but not + retained, so a stale sign-in cannot apply policy for an account that has already been + switched away from. + """ + + @staticmethod + def from_dict(obj: Any) -> '_ManagedSettingsResolveStartupPolicyParams': + assert isinstance(obj, dict) + environment = obj.get("environment") + home_directory = from_str(obj.get("homeDirectory")) + platform = from_str(obj.get("platform")) + retain_and_apply = from_bool(obj.get("retainAndApply")) + user_agent = from_str(obj.get("userAgent")) + auth_info = obj.get("authInfo") + bypass_cache_read = from_union([from_bool, from_none], obj.get("bypassCacheRead")) + client_name = from_union([from_str, from_none], obj.get("clientName")) + device_response = obj.get("deviceResponse") + has_device_response = from_union([from_bool, from_none], obj.get("hasDeviceResponse")) + policy_helper_enabled = from_union([from_bool, from_none], obj.get("policyHelperEnabled")) + policy_helper_reason = from_union([_ManagedSettingsPolicyHelperReason, from_none], obj.get("policyHelperReason")) + policy_helper_working_directory = from_union([from_str, from_none], obj.get("policyHelperWorkingDirectory")) + self_fetch = from_union([from_bool, from_none], obj.get("selfFetch")) + server_policy_cache_mode = from_union([_ManagedSettingsServerPolicyCacheMode, from_none], obj.get("serverPolicyCacheMode")) + session_id = from_union([from_str, from_none], obj.get("sessionId")) + return _ManagedSettingsResolveStartupPolicyParams(environment, home_directory, platform, retain_and_apply, user_agent, auth_info, bypass_cache_read, client_name, device_response, has_device_response, policy_helper_enabled, policy_helper_reason, policy_helper_working_directory, self_fetch, server_policy_cache_mode, session_id) + + def to_dict(self) -> dict: + result: dict = {} + result["environment"] = self.environment + result["homeDirectory"] = from_str(self.home_directory) + result["platform"] = from_str(self.platform) + result["retainAndApply"] = from_bool(self.retain_and_apply) + result["userAgent"] = from_str(self.user_agent) + if self.auth_info is not None: + result["authInfo"] = self.auth_info + if self.bypass_cache_read is not None: + result["bypassCacheRead"] = from_union([from_bool, from_none], self.bypass_cache_read) + if self.client_name is not None: + result["clientName"] = from_union([from_str, from_none], self.client_name) + if self.device_response is not None: + result["deviceResponse"] = self.device_response + if self.has_device_response is not None: + result["hasDeviceResponse"] = from_union([from_bool, from_none], self.has_device_response) + if self.policy_helper_enabled is not None: + result["policyHelperEnabled"] = from_union([from_bool, from_none], self.policy_helper_enabled) + if self.policy_helper_reason is not None: + result["policyHelperReason"] = from_union([lambda x: to_enum(_ManagedSettingsPolicyHelperReason, x), from_none], self.policy_helper_reason) + if self.policy_helper_working_directory is not None: + result["policyHelperWorkingDirectory"] = from_union([from_str, from_none], self.policy_helper_working_directory) + if self.self_fetch is not None: + result["selfFetch"] = from_union([from_bool, from_none], self.self_fetch) + if self.server_policy_cache_mode is not None: + result["serverPolicyCacheMode"] = from_union([lambda x: to_enum(_ManagedSettingsServerPolicyCacheMode, x), from_none], self.server_policy_cache_mode) + if self.session_id is not None: + result["sessionId"] = from_union([from_str, from_none], self.session_id) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _ManagedSettingsLogMessage: + """One diagnostic emitted while ingesting managed settings. Returned rather than logged by + the runtime so the host decides where ingestion diagnostics go, which matters because an + ingestion runs before a session exists to carry a log sink. + """ + level: _ManagedSettingsLogLevel + """Severity of this diagnostic.""" + + message: str + """Human-readable diagnostic text.""" + + @staticmethod + def from_dict(obj: Any) -> '_ManagedSettingsLogMessage': + assert isinstance(obj, dict) + level = _ManagedSettingsLogLevel(obj.get("level")) + message = from_str(obj.get("message")) + return _ManagedSettingsLogMessage(level, message) + + def to_dict(self) -> dict: + result: dict = {} + result["level"] = to_enum(_ManagedSettingsLogLevel, self.level) + result["message"] = from_str(self.message) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _ManagedSettingsResolveStartupPolicyResult: + """The startup policy a resolution composed, the per-layer outcome flags a host needs to + decide how much to trust it, and the enterprise sign-in verdict for the credential it ran + against. The flags are reported rather than folded into the settings because a host must + distinguish an unmanaged device from a managed device whose policy could not be + confirmed, and apply its fail-closed gates in the second case. + """ + device_load_failed: bool + """The device layer could not be read, so it is unconfirmed rather than known-absent.""" + + force_login_orgs: _ForceLoginOrgsDecision + """The enterprise sign-in verdict for the credential this resolution ran against, evaluated + over the effective policy so a server-delivered pin is honored alongside a device one. + Computed on every resolution, including one that was not retained, so a host that + declines to apply policy still cannot proceed past a pin. + """ + managed_policy_present: bool + """A managed policy is known to apply to this host, from a discovered source or a resolved + document. + """ + permission_policy_undetermined: bool + """The permissions layer specifically could not be confirmed, so permission gates apply + their fail-closed posture. + """ + policy_helper_load_failed: bool + """The policy helper was configured but could not be run, or its output could not be read.""" + + policy_undetermined: bool + """Some layer of the policy could not be confirmed, so its absence must not be read as + permission. Folds in a failed server fetch as well as an unreadable device layer. + """ + sandbox_enabled_by_undetermined_policy: bool + """The sandbox was enabled because policy could not be confirmed rather than because an + administrator asked for it. Lets a host explain a sandbox the user did not configure. + """ + server_fetch_failed: bool + """The server fetch failed and no cached entry stood in for it, so the server layer is + unconfirmed and fail-closed gates apply. + """ + server_resolution: _ManagedSettingsServerPolicyResolution + """How the current runtime resolved the server layer.""" + + telemetry: Any + """Telemetry describing which layers answered and how, for the host to report alongside its + own startup events. + """ + warnings: list[str] + """Warnings raised while resolving, in the order they occurred.""" + + client_update_requested: str | None = None + """Advisory minimum client version from a successful server response.""" + + client_update_required: str | None = None + """The server refused to deliver a policy because this client is too old to enforce it, + carrying the user-facing sentence describing the refusal. No retry will help; the remedy + is a client update. + """ + device_response: Any = None + """The device layer discovered from the machine, when one exists.""" + + device_sandbox_floor: Any = None + """The sandbox floor contributed by the device layer.""" + + policy_helper_response: Any = None + """The layer produced by the platform policy helper, when one ran.""" + + sandbox_floor: Any = None + """The effective sandbox floor composed across every layer, which a host must apply rather + than its own default. + """ + server_response: Any = None + """The account's server-delivered policy, when one was resolved.""" + + @staticmethod + def from_dict(obj: Any) -> '_ManagedSettingsResolveStartupPolicyResult': + assert isinstance(obj, dict) + device_load_failed = from_bool(obj.get("deviceLoadFailed")) + force_login_orgs = _ForceLoginOrgsDecision.from_dict(obj.get("forceLoginOrgs")) + managed_policy_present = from_bool(obj.get("managedPolicyPresent")) + permission_policy_undetermined = from_bool(obj.get("permissionPolicyUndetermined")) + policy_helper_load_failed = from_bool(obj.get("policyHelperLoadFailed")) + policy_undetermined = from_bool(obj.get("policyUndetermined")) + sandbox_enabled_by_undetermined_policy = from_bool(obj.get("sandboxEnabledByUndeterminedPolicy")) + server_fetch_failed = from_bool(obj.get("serverFetchFailed")) + server_resolution = _ManagedSettingsServerPolicyResolution(obj.get("serverResolution")) + telemetry = obj.get("telemetry") + warnings = from_list(from_str, obj.get("warnings")) + client_update_requested = from_union([from_str, from_none], obj.get("clientUpdateRequested")) + client_update_required = from_union([from_str, from_none], obj.get("clientUpdateRequired")) + device_response = obj.get("deviceResponse") + device_sandbox_floor = obj.get("deviceSandboxFloor") + policy_helper_response = obj.get("policyHelperResponse") + sandbox_floor = obj.get("sandboxFloor") + server_response = obj.get("serverResponse") + return _ManagedSettingsResolveStartupPolicyResult(device_load_failed, force_login_orgs, managed_policy_present, permission_policy_undetermined, policy_helper_load_failed, policy_undetermined, sandbox_enabled_by_undetermined_policy, server_fetch_failed, server_resolution, telemetry, warnings, client_update_requested, client_update_required, device_response, device_sandbox_floor, policy_helper_response, sandbox_floor, server_response) + + def to_dict(self) -> dict: + result: dict = {} + result["deviceLoadFailed"] = from_bool(self.device_load_failed) + result["forceLoginOrgs"] = to_class(_ForceLoginOrgsDecision, self.force_login_orgs) + result["managedPolicyPresent"] = from_bool(self.managed_policy_present) + result["permissionPolicyUndetermined"] = from_bool(self.permission_policy_undetermined) + result["policyHelperLoadFailed"] = from_bool(self.policy_helper_load_failed) + result["policyUndetermined"] = from_bool(self.policy_undetermined) + result["sandboxEnabledByUndeterminedPolicy"] = from_bool(self.sandbox_enabled_by_undetermined_policy) + result["serverFetchFailed"] = from_bool(self.server_fetch_failed) + result["serverResolution"] = to_enum(_ManagedSettingsServerPolicyResolution, self.server_resolution) + result["telemetry"] = self.telemetry + result["warnings"] = from_list(from_str, self.warnings) + if self.client_update_requested is not None: + result["clientUpdateRequested"] = from_union([from_str, from_none], self.client_update_requested) + if self.client_update_required is not None: + result["clientUpdateRequired"] = from_union([from_str, from_none], self.client_update_required) + if self.device_response is not None: + result["deviceResponse"] = self.device_response + if self.device_sandbox_floor is not None: + result["deviceSandboxFloor"] = self.device_sandbox_floor + if self.policy_helper_response is not None: + result["policyHelperResponse"] = self.policy_helper_response + if self.sandbox_floor is not None: + result["sandboxFloor"] = self.sandbox_floor + if self.server_response is not None: + result["serverResponse"] = self.server_response + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MarketplaceListResult: @@ -25957,6 +29624,45 @@ def to_dict(self) -> dict: result["mode"] = to_enum(MCPSetEnvValueModeDetails, self.mode) return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _MCPShouldExcludeGitHubToolsRequest: + """The session tool filters that decide whether the session still has a shell to run `gh` + with. + """ + available_tools: list[str] | None = None + """The session's tool allowlist, when it set one. Omitted means the session constrains + nothing this way. + """ + excluded_tools: list[str] | None = None + """The session's tool denylist, when it set one. Omitted means the session constrains + nothing this way. + """ + tool_filter_precedence: OptionsUpdateToolFilterPrecedence | None = None + """How the allowlist and denylist combine when both are set. Omitted means the default every + session gets, so a caller that never chose a precedence is answered as its sessions + behave. + """ + + @staticmethod + def from_dict(obj: Any) -> '_MCPShouldExcludeGitHubToolsRequest': + assert isinstance(obj, dict) + available_tools = from_union([lambda x: from_list(from_str, x), from_none], obj.get("availableTools")) + excluded_tools = from_union([lambda x: from_list(from_str, x), from_none], obj.get("excludedTools")) + tool_filter_precedence = from_union([from_none, OptionsUpdateToolFilterPrecedence], obj.get("toolFilterPrecedence")) + return _MCPShouldExcludeGitHubToolsRequest(available_tools, excluded_tools, tool_filter_precedence) + + def to_dict(self) -> dict: + result: dict = {} + if self.available_tools is not None: + result["availableTools"] = from_union([lambda x: from_list(from_str, x), from_none], self.available_tools) + if self.excluded_tools is not None: + result["excludedTools"] = from_union([lambda x: from_list(from_str, x), from_none], self.excluded_tools) + if self.tool_filter_precedence is not None: + result["toolFilterPrecedence"] = from_union([from_none, lambda x: to_enum(OptionsUpdateToolFilterPrecedence, x)], self.tool_filter_precedence) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ConnectorDiscoveryCatalogEntry: @@ -28105,6 +31811,36 @@ def to_dict(self) -> dict: result["version"] = from_union([from_str, from_none], self.version) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class ManagedPluginRetryEntry: + """Retry outcome for one plugin required by managed settings.""" + + spec: str + """Plugin spec in "plugin@marketplace" form.""" + + status: ManagedPluginRetryStatus + """What the retry did for this plugin.""" + + error: str | None = None + """Why the plugin was not prepared. Present when `status` is `failed` or `deferred`.""" + + @staticmethod + def from_dict(obj: Any) -> 'ManagedPluginRetryEntry': + assert isinstance(obj, dict) + spec = from_str(obj.get("spec")) + status = ManagedPluginRetryStatus(obj.get("status")) + error = from_union([from_str, from_none], obj.get("error")) + return ManagedPluginRetryEntry(spec, status, error) + + def to_dict(self) -> dict: + result: dict = {} + result["spec"] = from_str(self.spec) + result["status"] = to_enum(ManagedPluginRetryStatus, self.status) + if self.error is not None: + result["error"] = from_union([from_str, from_none], self.error) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MarketplacePluginInfo: @@ -29752,6 +33488,91 @@ def to_dict(self) -> dict: result["mode"] = from_union([lambda x: to_enum(RemoteSessionMode, x), from_none], self.mode) return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _UserSettingsEditRequest: + """One atomic edit of a canonical setting in settings.json.""" + + canonical_path: list[str] + """Nonempty canonical path segments of the setting to edit, starting with its top-level key.""" + + operation: _SettingsEditOperation + """Set a scalar, add or remove a string list item, or remove the setting. The handler + rejects a missing or incompatible value. + """ + config_dir: str | None = None + """Configuration directory to edit. Omitted or empty resolves the server's own home, as in + `user.settings.loadForConfigDir`. + """ + value: Any = UNSET_SENTINEL + """Required boolean, string or number for set, required string for add-item and remove-item, + and forbidden for remove. The runtime rejects all other JSON values. + """ + + @staticmethod + def from_dict(obj: Any) -> '_UserSettingsEditRequest': + assert isinstance(obj, dict) + canonical_path = from_list(from_str, obj.get("canonicalPath")) + operation = _SettingsEditOperation(obj.get("operation")) + config_dir = from_union([from_str, from_none], obj.get("configDir")) + value = obj["value"] if "value" in obj else UNSET_SENTINEL + return _UserSettingsEditRequest(canonical_path, operation, config_dir, value) + + def to_dict(self) -> dict: + result: dict = {} + result["canonicalPath"] = from_list(from_str, self.canonical_path) + result["operation"] = to_enum(_SettingsEditOperation, self.operation) + if self.config_dir is not None: + result["configDir"] = from_union([from_str, from_none], self.config_dir) + if self.value is not UNSET_SENTINEL: + result["value"] = self.value + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _RepoSettingsEditRequest: + """One atomic edit of an allowed top-level repository setting.""" + + key: str + """One canonical top-level repository setting that the allowlist permits.""" + + operation: _SettingsEditOperation + """Set a scalar, add or remove a string list item, or remove the setting. The handler + rejects a missing or incompatible value. + """ + repo_root: str + """Absolute path of the repository root.""" + + scope: _RepoSettingsEditScope + """Shared or local repository settings file.""" + + value: Any = None + """Required boolean, string or number for set, required string for add-item and remove-item, + and forbidden for remove. The runtime rejects all other JSON values. + """ + + @staticmethod + def from_dict(obj: Any) -> '_RepoSettingsEditRequest': + assert isinstance(obj, dict) + key = from_str(obj.get("key")) + operation = _SettingsEditOperation(obj.get("operation")) + repo_root = from_str(obj.get("repoRoot")) + scope = _RepoSettingsEditScope(obj.get("scope")) + value = obj.get("value") + return _RepoSettingsEditRequest(key, operation, repo_root, scope, value) + + def to_dict(self) -> dict: + result: dict = {} + result["key"] = from_str(self.key) + result["operation"] = to_enum(_SettingsEditOperation, self.operation) + result["repoRoot"] = from_str(self.repo_root) + result["scope"] = to_enum(_RepoSettingsEditScope, self.scope) + if self.value is not None: + result["value"] = self.value + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ResponseFormat: @@ -29894,6 +33715,59 @@ def to_dict(self) -> dict: result["proxy"] = from_union([lambda x: to_class(SandboxConfigUserPolicyNetworkProxy, x), from_none], self.proxy) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SandboxCredentialSuggestionsResult: + """Possible unconfigured secrets in the sandbox shell environment, sorted by variable name, + with no secret values. + """ + suggestions: list[SandboxCredentialSuggestion] + """Candidates for user review. Empty when sandboxing is disabled or no unconfigured + candidates are present. + """ + + @staticmethod + def from_dict(obj: Any) -> 'SandboxCredentialSuggestionsResult': + assert isinstance(obj, dict) + suggestions = from_list(SandboxCredentialSuggestion.from_dict, obj.get("suggestions")) + return SandboxCredentialSuggestionsResult(suggestions) + + def to_dict(self) -> dict: + result: dict = {} + result["suggestions"] = from_list(lambda x: to_class(SandboxCredentialSuggestion, x), self.suggestions) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _SandboxFilterPickerCandidatesParams: + """A batch of file-picker candidates to check against a prepared policy.""" + + candidates: list[_SandboxPickerCandidate] + """The candidates to check, in the order the answers are returned.""" + + cwd: str + """The session's working directory, matching the one the policy was prepared from.""" + + sandbox: Any = None + """The policy returned by `sandbox.preparePickerPolicy`. Omitted keeps every candidate.""" + + @staticmethod + def from_dict(obj: Any) -> '_SandboxFilterPickerCandidatesParams': + assert isinstance(obj, dict) + candidates = from_list(_SandboxPickerCandidate.from_dict, obj.get("candidates")) + cwd = from_str(obj.get("cwd")) + sandbox = obj.get("sandbox") + return _SandboxFilterPickerCandidatesParams(candidates, cwd, sandbox) + + def to_dict(self) -> dict: + result: dict = {} + result["candidates"] = from_list(lambda x: to_class(_SandboxPickerCandidate, x), self.candidates) + result["cwd"] = from_str(self.cwd) + if self.sandbox is not None: + result["sandbox"] = self.sandbox + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SandboxHostSupport: @@ -31118,6 +34992,9 @@ class AgentInfo: disable_model_invocation: bool | None = None """Whether model-driven invocation is disabled for this agent.""" + hooks: dict[str, list[Any]] | None = None + """Declarative hook actions keyed by event name, executed only while this agent is active.""" + mcp_servers: dict[str, Any] | None = None """MCP server configurations attached to this agent, keyed by server name. Server config shape mirrors the MCP `mcpServers` schema. @@ -31167,6 +35044,7 @@ def from_dict(obj: Any) -> 'AgentInfo': id = from_str(obj.get("id")) name = from_str(obj.get("name")) disable_model_invocation = from_union([from_bool, from_none], obj.get("disableModelInvocation")) + hooks = from_union([lambda x: from_dict(lambda x: from_list(lambda x: x, x), x), from_none], obj.get("hooks")) mcp_servers = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("mcpServers")) model = from_union([from_str, from_none], obj.get("model")) model_policy = from_union([AgentModelPolicy, from_none], obj.get("modelPolicy")) @@ -31178,7 +35056,7 @@ def from_dict(obj: Any) -> 'AgentInfo': source = from_union([AgentInfoSource, from_none], obj.get("source")) tools = from_union([lambda x: from_list(from_str, x), from_none], obj.get("tools")) user_invocable = from_union([from_bool, from_none], obj.get("userInvocable")) - return AgentInfo(description, display_name, id, name, disable_model_invocation, mcp_servers, model, model_policy, models, path, prompt, reasoning_effort, skills, source, tools, user_invocable) + return AgentInfo(description, display_name, id, name, disable_model_invocation, hooks, mcp_servers, model, model_policy, models, path, prompt, reasoning_effort, skills, source, tools, user_invocable) def to_dict(self) -> dict: result: dict = {} @@ -31188,6 +35066,8 @@ def to_dict(self) -> dict: result["name"] = from_str(self.name) if self.disable_model_invocation is not None: result["disableModelInvocation"] = from_union([from_bool, from_none], self.disable_model_invocation) + if self.hooks is not None: + result["hooks"] = from_union([lambda x: from_dict(lambda x: from_list(lambda x: x, x), x), from_none], self.hooks) if self.mcp_servers is not None: result["mcpServers"] = from_union([lambda x: from_dict(lambda x: x, x), from_none], self.mcp_servers) if self.model is not None: @@ -31787,6 +35667,69 @@ def to_dict(self) -> dict: result["runtimeSettingsChanged"] = from_union([from_bool, from_none], self.runtime_settings_changed) return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _UserSettingsMigrateStaffLogLevelOnceResult: + """The outcome of the one-time staff log-level migration.""" + + status: StaffLogLevelMigrationStatus + """What the migration did. Only `refreshed` can have changed the persisted settings.""" + + completed_at: str | None = None + """When the migration was recorded as done, as an ISO-8601 timestamp. Absent only for + `ineligible`, which records nothing. + """ + settings: dict[str, Any] | None = None + """The settings document as it stands after the migration, keyed by setting name. Present + only for `refreshed`, and only when the document could be re-read cleanly. + """ + + @staticmethod + def from_dict(obj: Any) -> '_UserSettingsMigrateStaffLogLevelOnceResult': + assert isinstance(obj, dict) + status = StaffLogLevelMigrationStatus(obj.get("status")) + completed_at = from_union([from_str, from_none], obj.get("completedAt")) + settings = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("settings")) + return _UserSettingsMigrateStaffLogLevelOnceResult(status, completed_at, settings) + + def to_dict(self) -> dict: + result: dict = {} + result["status"] = to_enum(StaffLogLevelMigrationStatus, self.status) + if self.completed_at is not None: + result["completedAt"] = from_union([from_str, from_none], self.completed_at) + if self.settings is not None: + result["settings"] = from_union([lambda x: from_dict(lambda x: x, x), from_none], self.settings) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _UserSettingsMigrateStaffUpdateChannelOnceResult: + """The outcome of the one-time staff update-channel migration.""" + + status: StaffUpdateChannelMigrationStatus + """What the migration did. Only `migrated` means the persisted settings changed.""" + + completed_at: str | None = None + """When the migration was recorded as done, as an ISO-8601 timestamp. Absent only for + `ineligible`, which records nothing. + """ + + @staticmethod + def from_dict(obj: Any) -> '_UserSettingsMigrateStaffUpdateChannelOnceResult': + assert isinstance(obj, dict) + status = StaffUpdateChannelMigrationStatus(obj.get("status")) + completed_at = from_union([from_str, from_none], obj.get("completedAt")) + return _UserSettingsMigrateStaffUpdateChannelOnceResult(status, completed_at) + + def to_dict(self) -> dict: + result: dict = {} + result["status"] = to_enum(StaffUpdateChannelMigrationStatus, self.status) + if self.completed_at is not None: + result["completedAt"] = from_union([from_str, from_none], self.completed_at) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class TaskAgentProgress: @@ -32712,6 +36655,251 @@ def to_dict(self) -> dict: result["settings"] = from_dict(lambda x: to_class(UserSettingMetadata, x), self.settings) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsApplyTopKeyResult: + """Outcome of validating and writing one top-level setting subtree.""" + + status: str + """`ok` when the value was written, `validation-error` when it was rejected before any + write, `write-error` when validation passed but the write failed. + """ + error: str | None = None + """Why the write failed, present only when `status` is `write-error`.""" + + issues: list[UserSettingsValidationIssue] | None = None + """Problems found, present only when `status` is `validation-error`.""" + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsApplyTopKeyResult': + assert isinstance(obj, dict) + status = from_str(obj.get("status")) + error = from_union([from_none, from_str], obj.get("error")) + issues = from_union([lambda x: from_list(UserSettingsValidationIssue.from_dict, x), from_none], obj.get("issues")) + return UserSettingsApplyTopKeyResult(status, error, issues) + + def to_dict(self) -> dict: + result: dict = {} + result["status"] = from_str(self.status) + if self.error is not None: + result["error"] = from_union([from_none, from_str], self.error) + if self.issues is not None: + result["issues"] = from_union([lambda x: from_list(lambda x: to_class(UserSettingsValidationIssue, x), x), from_none], self.issues) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsValidateResult: + """Outcome of validating one top-level setting subtree.""" + + ok: bool + """True when the subtree is valid.""" + + issues: list[UserSettingsValidationIssue] | None = None + """Problems found, empty when `ok` is true.""" + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsValidateResult': + assert isinstance(obj, dict) + ok = from_bool(obj.get("ok")) + issues = from_union([lambda x: from_list(UserSettingsValidationIssue.from_dict, x), from_none], obj.get("issues")) + return UserSettingsValidateResult(ok, issues) + + def to_dict(self) -> dict: + result: dict = {} + result["ok"] = from_bool(self.ok) + if self.issues is not None: + result["issues"] = from_union([lambda x: from_list(lambda x: to_class(UserSettingsValidationIssue, x), x), from_none], self.issues) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _UserSettingsMigrateStaffLogLevelOnceRequest: + """Selects the configuration directory to run the one-time staff log-level migration + against, and supplies the caller's record of whether it has already run. + """ + context: UserSettingsHostContext + """The configuration directory, home directory and environment to resolve the settings and + global-state documents from. A host that scopes a session to its own Copilot home must + pass that directory here, or the migration is evaluated against the server's own home + instead. + """ + cached_completed_at: str | None = None + """When the caller last recorded this migration, as an ISO-8601 timestamp. Supplying it lets + the runtime answer `current` without re-running the evaluation. + """ + + @staticmethod + def from_dict(obj: Any) -> '_UserSettingsMigrateStaffLogLevelOnceRequest': + assert isinstance(obj, dict) + context = UserSettingsHostContext.from_dict(obj.get("context")) + cached_completed_at = from_union([from_str, from_none], obj.get("cachedCompletedAt")) + return _UserSettingsMigrateStaffLogLevelOnceRequest(context, cached_completed_at) + + def to_dict(self) -> dict: + result: dict = {} + result["context"] = to_class(UserSettingsHostContext, self.context) + if self.cached_completed_at is not None: + result["cachedCompletedAt"] = from_union([from_str, from_none], self.cached_completed_at) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _UserSettingsResetStaffModelSelectionOnceRequest: + """Selects the configuration directory to run the one-time staff model-selection reset + against, and supplies the caller's record of whether it has already run. + """ + context: UserSettingsHostContext + """The configuration directory, home directory and environment to resolve the settings and + global-state documents from. A host that scopes a session to its own Copilot home must + pass that directory here, or the reset is evaluated against the server's own home instead. + """ + staff: bool | None = None + """Whether the signed-in account is a staff account. The reset applies only to staff, so + anything other than `true` returns `didReset: false` without touching the settings + document. + """ + staff_model_reset_at: str | None = None + """When the caller last recorded this reset, as an ISO-8601 timestamp. A timestamp at or + after the cutoff means the reset already ran and is returned unchanged. + """ + + @staticmethod + def from_dict(obj: Any) -> '_UserSettingsResetStaffModelSelectionOnceRequest': + assert isinstance(obj, dict) + context = UserSettingsHostContext.from_dict(obj.get("context")) + staff = from_union([from_bool, from_none], obj.get("staff")) + staff_model_reset_at = from_union([from_str, from_none], obj.get("staffModelResetAt")) + return _UserSettingsResetStaffModelSelectionOnceRequest(context, staff, staff_model_reset_at) + + def to_dict(self) -> dict: + result: dict = {} + result["context"] = to_class(UserSettingsHostContext, self.context) + if self.staff is not None: + result["staff"] = from_union([from_bool, from_none], self.staff) + if self.staff_model_reset_at is not None: + result["staffModelResetAt"] = from_union([from_str, from_none], self.staff_model_reset_at) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _UserSettingsUnknownKeysWarningRequest: + """Selects the configuration directory whose user settings document should be inspected for + unrecognized top-level keys. + """ + context: UserSettingsHostContext + """The configuration directory, home directory and environment to resolve the settings + document from. A host that scopes a session to its own Copilot home must pass that + directory here, or the server's own home is inspected instead. + """ + + @staticmethod + def from_dict(obj: Any) -> '_UserSettingsUnknownKeysWarningRequest': + assert isinstance(obj, dict) + context = UserSettingsHostContext.from_dict(obj.get("context")) + return _UserSettingsUnknownKeysWarningRequest(context) + + def to_dict(self) -> dict: + result: dict = {} + result["context"] = to_class(UserSettingsHostContext, self.context) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _UserSettingsMigrateObsoleteOnDiskResult: + """The outcome of the one-time obsolete-settings cleanup.""" + + status: _UserSettingsMigrateObsoleteOnDiskStatus + """What the cleanup did. Only `changed` rewrote the document. The cleaned settings are not + returned here: the document is written before this replies, so `user.settings.load` reads + it authoritatively. + """ + + @staticmethod + def from_dict(obj: Any) -> '_UserSettingsMigrateObsoleteOnDiskResult': + assert isinstance(obj, dict) + status = _UserSettingsMigrateObsoleteOnDiskStatus(obj.get("status")) + return _UserSettingsMigrateObsoleteOnDiskResult(status) + + def to_dict(self) -> dict: + result: dict = {} + result["status"] = to_enum(_UserSettingsMigrateObsoleteOnDiskStatus, self.status) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsProblem: + """One setting that will not take effect, and why.""" + + category: str + """Coarse grouping for the problem, for a caller that presents problems in sections.""" + + explanation: str + """Why the setting will not take effect. Never contains the setting's value, which may be a + token or another secret. + """ + id: str + """Stable identifier for this problem within the reported set, so a caller can track a + selection across refreshes. + """ + kind: str + """What is wrong in machine-readable form, for a caller that wants to branch rather than + display. + """ + source: UserSettingsProblemLocation + """The settings layer the problem was found in.""" + + raw_key: str | None = None + """The offending key as written in the file, when the problem is attributable to a single + key. Absent when the whole file is at fault. + """ + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsProblem': + assert isinstance(obj, dict) + category = from_str(obj.get("category")) + explanation = from_str(obj.get("explanation")) + id = from_str(obj.get("id")) + kind = from_str(obj.get("kind")) + source = UserSettingsProblemLocation.from_dict(obj.get("source")) + raw_key = from_union([from_str, from_none], obj.get("rawKey")) + return UserSettingsProblem(category, explanation, id, kind, source, raw_key) + + def to_dict(self) -> dict: + result: dict = {} + result["category"] = from_str(self.category) + result["explanation"] = from_str(self.explanation) + result["id"] = from_str(self.id) + result["kind"] = from_str(self.kind) + result["source"] = to_class(UserSettingsProblemLocation, self.source) + if self.raw_key is not None: + result["rawKey"] = from_union([from_str, from_none], self.raw_key) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsProblemsRequest: + """The settings layers to diagnose.""" + + sources: list[UserSettingsProblemSource] | None = None + """Layers to diagnose, in the precedence order the caller applies them.""" + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsProblemsRequest': + assert isinstance(obj, dict) + sources = from_union([lambda x: from_list(UserSettingsProblemSource.from_dict, x), from_none], obj.get("sources")) + return UserSettingsProblemsRequest(sources) + + def to_dict(self) -> dict: + result: dict = {} + if self.sources is not None: + result["sources"] = from_union([lambda x: from_list(lambda x: to_class(UserSettingsProblemSource, x), x), from_none], self.sources) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class WorkflowAgentRequest: @@ -33170,6 +37358,40 @@ def to_dict(self) -> dict: result["saved"] = from_union([lambda x: to_class(Saved, x), from_none], self.saved) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class ConnectorDiscoveryCapabilities: + """Feature availability.""" + + api_version: int + """API version.""" + + availability: ConnectorDiscoveryAvailability + """Availability.""" + + conditional_cache: bool + """Whether results are cached.""" + + opaque_account_selection: bool + """Whether accounts are selected by opaque ID.""" + + @staticmethod + def from_dict(obj: Any) -> 'ConnectorDiscoveryCapabilities': + assert isinstance(obj, dict) + api_version = from_int(obj.get("apiVersion")) + availability = ConnectorDiscoveryAvailability(obj.get("availability")) + conditional_cache = from_bool(obj.get("conditionalCache")) + opaque_account_selection = from_bool(obj.get("opaqueAccountSelection")) + return ConnectorDiscoveryCapabilities(api_version, availability, conditional_cache, opaque_account_selection) + + def to_dict(self) -> dict: + result: dict = {} + result["apiVersion"] = from_int(self.api_version) + result["availability"] = to_enum(ConnectorDiscoveryAvailability, self.availability) + result["conditionalCache"] = from_bool(self.conditional_cache) + result["opaqueAccountSelection"] = from_bool(self.opaque_account_selection) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ConnectorSessionAccount: @@ -35385,6 +39607,26 @@ def to_dict(self) -> dict: result["title"] = from_union([from_str, from_none], self.title) return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _FeatureFlagsMetadataResult: + """Every feature flag this runtime declares, with the shape each one was declared with.""" + + flags: dict[str, _FeatureFlagMetadata] + """Declared feature flags keyed by flag name.""" + + @staticmethod + def from_dict(obj: Any) -> '_FeatureFlagsMetadataResult': + assert isinstance(obj, dict) + flags = from_dict(_FeatureFlagMetadata.from_dict, obj.get("flags")) + return _FeatureFlagsMetadataResult(flags) + + def to_dict(self) -> dict: + result: dict = {} + result["flags"] = from_dict(lambda x: to_class(_FeatureFlagMetadata, x), self.flags) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MCPOauthHandlePendingRequest: @@ -36406,6 +40648,12 @@ class ManagedSettingsResolveResult: meta: ManagedSettingsMeta | None = None """Per-key lock state and provenance for the entries in `values`, using the same key names.""" + permissions_context: ManagedPermissionsContext | None = None + """Reusable source-composed permission policy for managedSettings.permissions.evaluate. + Unlike resolved.settings.permissions, this retains every source's allowlist and + default-prompt semantics. Refresh through resolve when account/device policy changes. + Absent on runtimes that do not support permission evaluation. + """ values: ManagedSettingsValues | None = None """Typed effective values of managed settings, keyed like the managed-settings schema and already resolved across channels, with the `{ "overridable": ... }` wrapper removed. @@ -36421,8 +40669,9 @@ def from_dict(obj: Any) -> 'ManagedSettingsResolveResult': resolved = ManagedSettingsResolvedData.from_dict(obj.get("resolved")) account = from_union([from_str, from_none], obj.get("account")) meta = from_union([ManagedSettingsMeta.from_dict, from_none], obj.get("meta")) + permissions_context = from_union([ManagedPermissionsContext.from_dict, from_none], obj.get("permissionsContext")) values = from_union([ManagedSettingsValues.from_dict, from_none], obj.get("values")) - return ManagedSettingsResolveResult(diagnostics, layers, resolved, account, meta, values) + return ManagedSettingsResolveResult(diagnostics, layers, resolved, account, meta, permissions_context, values) def to_dict(self) -> dict: result: dict = {} @@ -36433,10 +40682,191 @@ def to_dict(self) -> dict: result["account"] = from_union([from_str, from_none], self.account) if self.meta is not None: result["meta"] = from_union([lambda x: to_class(ManagedSettingsMeta, x), from_none], self.meta) + if self.permissions_context is not None: + result["permissionsContext"] = from_union([lambda x: to_class(ManagedPermissionsContext, x), from_none], self.permissions_context) if self.values is not None: result["values"] = from_union([lambda x: to_class(ManagedSettingsValues, x), from_none], self.values) return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _ManagedSettingsIngestResult: + """The managed policy layers an ingestion resolved, together with the per-layer outcome + flags a host needs to decide how much to trust the result. The flags are reported rather + than folded into the settings because a host must be able to distinguish an unmanaged + device from a managed device whose policy could not be confirmed, and apply its + fail-closed gates in the second case. + """ + device_load_failed: bool + """The device layer could not be read, so it is unconfirmed rather than known-absent.""" + + device_sandbox_undetermined: bool + """Whether the device layer's sandbox intent could not be determined, so a consumer must not + read its absence as permission to run unsandboxed. + """ + device_source_found: bool + """A device-managed policy is known to exist.""" + + log_messages: list[_ManagedSettingsLogMessage] + """Diagnostics emitted while resolving the layers, in the order they occurred.""" + + policy_helper_configured: bool + """A policy helper is configured for this host.""" + + policy_helper_fail_closed: bool + """The policy helper's failure must be treated as fail-closed rather than ignored.""" + + policy_helper_load_failed: bool + """The policy helper was configured but could not be run or its output could not be read.""" + + server_cache_fallback: bool + """A live fetch failed but a recent cached entry was served in its place, so + `serverFetchFailed` stays false. Lets a consumer distinguish a clean fetch from a + cache-backed fallback. + """ + server_fetch_failed: bool + """The server fetch failed and no cached entry stood in for it, so the server layer is + unconfirmed and fail-closed gates apply. + """ + server_forced_refresh_failure: bool + """The server fetch failed while a policy demanded a fresh confirmation, which is the strict + posture an enterprise opted into rather than an ordinary transient failure. + """ + server_resolution: _ManagedSettingsServerPolicyResolution + """How the server layer was resolved.""" + + server_retryable_failure: bool + """The server fetch failed in a way a later retry could succeed at, as opposed to a refusal + that no retry will change. + """ + server_source_found: bool + """A server-managed policy is known to exist even though `serverResponse` is intentionally + unset. Consumers should OR this with the presence of `serverResponse` when deciding + whether the device is managed, so a fresh start during an outage still applies the + fail-closed gates instead of treating the device as unmanaged. + """ + server_token_unavailable: bool + """No usable token was available, so no server fetch was attempted.""" + + device_response: Any = None + """The device layer discovered from the machine, when one exists.""" + + device_sandbox_floor: Any = None + """The sandbox floor contributed by the device layer.""" + + permissions: Any = None + """The effective managed permissions layer, when the resolved policy carries one.""" + + policy_helper_authority: _ManagedSettingsPolicyHelperAuthority | None = None + """Which channel registered the policy helper that produced the helper layer.""" + + policy_helper_response: Any = None + """The layer produced by the platform policy helper, when one ran.""" + + server_client_update_requested: str | None = None + """Minimum client version requested by a successful response. Advisory only; the delivered + policy remains active. + """ + server_client_update_required: str | None = None + """The server refused to deliver a policy because this client is too old to enforce it, + carrying the user-facing sentence describing the refusal. No retry will help; the remedy + is a client update. Gates that exist only because of managed policy should deny on this + rather than falling back to their unmanaged default. + """ + server_fetch_error: str | None = None + """The error that made the server fetch fail, when one did.""" + + server_permissions_floor: Any = None + """The synthesized fail-closed permissions layer applied in place of a policy a refused + client may not enforce. + """ + server_response: Any = None + """The account's server-delivered policy, when one was resolved.""" + + server_sandbox_floor: Any = None + """The sandbox object from a cached server policy that a forced refresh declined to serve in + full. Folded back into the effective sandbox layer so an outage during a forced refresh + still synthesizes the fail-closed sandbox floor from the administrator's known intent. + """ + settings: Any = None + """The effective managed settings composed from every resolved layer. Omitted when no layer + set anything, so an unmanaged host locks nothing. + """ + + @staticmethod + def from_dict(obj: Any) -> '_ManagedSettingsIngestResult': + assert isinstance(obj, dict) + device_load_failed = from_bool(obj.get("deviceLoadFailed")) + device_sandbox_undetermined = from_bool(obj.get("deviceSandboxUndetermined")) + device_source_found = from_bool(obj.get("deviceSourceFound")) + log_messages = from_list(_ManagedSettingsLogMessage.from_dict, obj.get("logMessages")) + policy_helper_configured = from_bool(obj.get("policyHelperConfigured")) + policy_helper_fail_closed = from_bool(obj.get("policyHelperFailClosed")) + policy_helper_load_failed = from_bool(obj.get("policyHelperLoadFailed")) + server_cache_fallback = from_bool(obj.get("serverCacheFallback")) + server_fetch_failed = from_bool(obj.get("serverFetchFailed")) + server_forced_refresh_failure = from_bool(obj.get("serverForcedRefreshFailure")) + server_resolution = _ManagedSettingsServerPolicyResolution(obj.get("serverResolution")) + server_retryable_failure = from_bool(obj.get("serverRetryableFailure")) + server_source_found = from_bool(obj.get("serverSourceFound")) + server_token_unavailable = from_bool(obj.get("serverTokenUnavailable")) + device_response = obj.get("deviceResponse") + device_sandbox_floor = obj.get("deviceSandboxFloor") + permissions = obj.get("permissions") + policy_helper_authority = from_union([_ManagedSettingsPolicyHelperAuthority, from_none], obj.get("policyHelperAuthority")) + policy_helper_response = obj.get("policyHelperResponse") + server_client_update_requested = from_union([from_str, from_none], obj.get("serverClientUpdateRequested")) + server_client_update_required = from_union([from_str, from_none], obj.get("serverClientUpdateRequired")) + server_fetch_error = from_union([from_str, from_none], obj.get("serverFetchError")) + server_permissions_floor = obj.get("serverPermissionsFloor") + server_response = obj.get("serverResponse") + server_sandbox_floor = obj.get("serverSandboxFloor") + settings = obj.get("settings") + return _ManagedSettingsIngestResult(device_load_failed, device_sandbox_undetermined, device_source_found, log_messages, policy_helper_configured, policy_helper_fail_closed, policy_helper_load_failed, server_cache_fallback, server_fetch_failed, server_forced_refresh_failure, server_resolution, server_retryable_failure, server_source_found, server_token_unavailable, device_response, device_sandbox_floor, permissions, policy_helper_authority, policy_helper_response, server_client_update_requested, server_client_update_required, server_fetch_error, server_permissions_floor, server_response, server_sandbox_floor, settings) + + def to_dict(self) -> dict: + result: dict = {} + result["deviceLoadFailed"] = from_bool(self.device_load_failed) + result["deviceSandboxUndetermined"] = from_bool(self.device_sandbox_undetermined) + result["deviceSourceFound"] = from_bool(self.device_source_found) + result["logMessages"] = from_list(lambda x: to_class(_ManagedSettingsLogMessage, x), self.log_messages) + result["policyHelperConfigured"] = from_bool(self.policy_helper_configured) + result["policyHelperFailClosed"] = from_bool(self.policy_helper_fail_closed) + result["policyHelperLoadFailed"] = from_bool(self.policy_helper_load_failed) + result["serverCacheFallback"] = from_bool(self.server_cache_fallback) + result["serverFetchFailed"] = from_bool(self.server_fetch_failed) + result["serverForcedRefreshFailure"] = from_bool(self.server_forced_refresh_failure) + result["serverResolution"] = to_enum(_ManagedSettingsServerPolicyResolution, self.server_resolution) + result["serverRetryableFailure"] = from_bool(self.server_retryable_failure) + result["serverSourceFound"] = from_bool(self.server_source_found) + result["serverTokenUnavailable"] = from_bool(self.server_token_unavailable) + if self.device_response is not None: + result["deviceResponse"] = self.device_response + if self.device_sandbox_floor is not None: + result["deviceSandboxFloor"] = self.device_sandbox_floor + if self.permissions is not None: + result["permissions"] = self.permissions + if self.policy_helper_authority is not None: + result["policyHelperAuthority"] = from_union([lambda x: to_enum(_ManagedSettingsPolicyHelperAuthority, x), from_none], self.policy_helper_authority) + if self.policy_helper_response is not None: + result["policyHelperResponse"] = self.policy_helper_response + if self.server_client_update_requested is not None: + result["serverClientUpdateRequested"] = from_union([from_str, from_none], self.server_client_update_requested) + if self.server_client_update_required is not None: + result["serverClientUpdateRequired"] = from_union([from_str, from_none], self.server_client_update_required) + if self.server_fetch_error is not None: + result["serverFetchError"] = from_union([from_str, from_none], self.server_fetch_error) + if self.server_permissions_floor is not None: + result["serverPermissionsFloor"] = self.server_permissions_floor + if self.server_response is not None: + result["serverResponse"] = self.server_response + if self.server_sandbox_floor is not None: + result["serverSandboxFloor"] = self.server_sandbox_floor + if self.settings is not None: + result["settings"] = self.settings + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MCPAppsHostContext: @@ -37457,6 +41887,27 @@ def to_dict(self) -> dict: result["plugins"] = from_list(lambda x: to_class(InstalledPluginInfo, x), self.plugins) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SessionPluginsRetryManagedResult: + """Per-plugin outcomes of retrying the plugins required by the session's managed settings.""" + + plugins: list[ManagedPluginRetryEntry] + """One entry per requested plugin, in request order. When no plugins were requested, one + entry per plugin the managed settings require. + """ + + @staticmethod + def from_dict(obj: Any) -> 'SessionPluginsRetryManagedResult': + assert isinstance(obj, dict) + plugins = from_list(ManagedPluginRetryEntry.from_dict, obj.get("plugins")) + return SessionPluginsRetryManagedResult(plugins) + + def to_dict(self) -> dict: + result: dict = {} + result["plugins"] = from_list(lambda x: to_class(ManagedPluginRetryEntry, x), self.plugins) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MarketplaceBrowseResult: @@ -39661,6 +44112,26 @@ def to_dict(self) -> dict: result["response"] = to_class(UIExitPlanModeResponse, self.response) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsProblemsResult: + """The settings that will not take effect across the given layers.""" + + problems: list[UserSettingsProblem] | None = None + """Each setting that will not take effect, and why. Empty when every layer is healthy.""" + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsProblemsResult': + assert isinstance(obj, dict) + problems = from_union([lambda x: from_list(UserSettingsProblem.from_dict, x), from_none], obj.get("problems")) + return UserSettingsProblemsResult(problems) + + def to_dict(self) -> dict: + result: dict = {} + if self.problems is not None: + result["problems"] = from_union([lambda x: from_list(lambda x: to_class(UserSettingsProblem, x), x), from_none], self.problems) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class WorkflowLogRequest: @@ -41118,6 +45589,30 @@ def to_dict(self) -> dict: result["session"] = from_union([lambda x: to_class(CanvasSessionContext, x), from_none], self.session) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class ConnectorDiscoveryAccountList: + """Eligible accounts.""" + + accounts: list[ConnectorDiscoveryAccount] + """Eligible accounts.""" + + availability: ConnectorDiscoveryAvailability + """Availability.""" + + @staticmethod + def from_dict(obj: Any) -> 'ConnectorDiscoveryAccountList': + assert isinstance(obj, dict) + accounts = from_list(ConnectorDiscoveryAccount.from_dict, obj.get("accounts")) + availability = ConnectorDiscoveryAvailability(obj.get("availability")) + return ConnectorDiscoveryAccountList(accounts, availability) + + def to_dict(self) -> dict: + result: dict = {} + result["accounts"] = from_list(lambda x: to_class(ConnectorDiscoveryAccount, x), self.accounts) + result["availability"] = to_enum(ConnectorDiscoveryAvailability, self.availability) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class EnvironmentsGetResult: @@ -41358,9 +45853,6 @@ class _GlobalStateLoadResult: recorded nothing yet, so a reader must treat an absent field as `not yet`, never as a negative answer. Stored credentials are deliberately absent from this shape. """ - app_install_nudge_responded: bool | None = None - """Whether the user has answered the prompt suggesting they install the desktop app.""" - app_tip_shown: bool | None = None """Whether the app tip has been shown.""" @@ -41423,7 +45915,6 @@ class _GlobalStateLoadResult: @staticmethod def from_dict(obj: Any) -> '_GlobalStateLoadResult': assert isinstance(obj, dict) - app_install_nudge_responded = from_union([from_bool, from_none], obj.get("appInstallNudgeResponded")) app_tip_shown = from_union([from_bool, from_none], obj.get("appTipShown")) asked_setup_terminals = from_union([lambda x: from_list(from_str, x), from_none], obj.get("askedSetupTerminals")) auto_feedback_last_prompted_at = from_union([from_str, from_none], obj.get("autoFeedbackLastPromptedAt")) @@ -41443,12 +45934,10 @@ def from_dict(obj: Any) -> '_GlobalStateLoadResult': staff_update_channel_migration_at = from_union([from_str, from_none], obj.get("staffUpdateChannelMigrationAt")) suppress_init_folders = from_union([lambda x: from_list(from_str, x), from_none], obj.get("suppressInitFolders")) trusted_folders = from_union([lambda x: from_list(from_str, x), from_none], obj.get("trustedFolders")) - return _GlobalStateLoadResult(app_install_nudge_responded, app_tip_shown, asked_setup_terminals, auto_feedback_last_prompted_at, first_launch_at, installed_plugins, last_logged_in_user, logged_in_users, reasoning_summaries_cleanup_done, recent_model_ids, sandbox_credential_proxy_ca_declined, sandbox_onboarding_shown, staff, staff_github, staff_log_level_migration_at, staff_microsoft, staff_model_reset_at, staff_update_channel_migration_at, suppress_init_folders, trusted_folders) + return _GlobalStateLoadResult(app_tip_shown, asked_setup_terminals, auto_feedback_last_prompted_at, first_launch_at, installed_plugins, last_logged_in_user, logged_in_users, reasoning_summaries_cleanup_done, recent_model_ids, sandbox_credential_proxy_ca_declined, sandbox_onboarding_shown, staff, staff_github, staff_log_level_migration_at, staff_microsoft, staff_model_reset_at, staff_update_channel_migration_at, suppress_init_folders, trusted_folders) def to_dict(self) -> dict: result: dict = {} - if self.app_install_nudge_responded is not None: - result["appInstallNudgeResponded"] = from_union([from_bool, from_none], self.app_install_nudge_responded) if self.app_tip_shown is not None: result["appTipShown"] = from_union([from_bool, from_none], self.app_tip_shown) if self.asked_setup_terminals is not None: @@ -43879,6 +48368,13 @@ class SessionUpdateOptionsParams: agent_context: str | None = None """Runtime context discriminator (e.g., `cli`, `actions`).""" + agent_directories: list[str] | None = None + """Explicit discovery-only agent roots, separate from customAgents and filesystem grants. + Available without enableConfigDiscovery, subject to managed/source disables. Relative SDK + paths resolve once when supplied against the request's effective workingDirectory; + retained roots do not move on cwd changes. Null and omission are equivalent; [] clears + only the explicit SDK contribution. + """ allow_all_mcp_server_instructions: bool | None = None """Whether to include instructions from every MCP server in the system prompt instead of only allowlisted servers. @@ -43919,8 +48415,9 @@ class SessionUpdateOptionsParams: """Skill IDs that should be excluded from this session.""" enable_file_hooks: bool | None = None - """Whether to enable loading of `.github/hooks/` filesystem hooks. Separate from the SDK - callback hook mechanism. + """Whether to enable repository-origin file hooks from `.github/hooks/` and trusted + repository-configured roots. Does not control explicit hookDirectories or SDK callback + hooks. """ enable_host_git_operations: bool | None = None """Whether to enable host git operations (context resolution, child repo scanning, git info @@ -43941,9 +48438,10 @@ class SessionUpdateOptionsParams: """Whether to enable cross-session store writes and reads.""" enable_skills: bool | None = None - """Whether skill loading is enabled. Explicit false disables every source, including a bound - SDK provider; changing the value invalidates the loaded skill snapshot. When omitted, - creation falls back to enableConfigDiscovery unless an SDK skill provider is registered. + """Whether skill loading is enabled. Explicit false disables every source, including + explicit SDK roots and a bound SDK provider; changing the value invalidates the loaded + skill snapshot. When omitted, creation falls back to enableConfigDiscovery unless + explicit SDK skill roots or an SDK skill provider are supplied. """ enable_streaming: bool | None = None """Whether to stream model responses.""" @@ -43970,6 +48468,14 @@ class SessionUpdateOptionsParams: feature_flags: dict[str, bool] | None = None """Map of feature-flag IDs to their boolean enabled state.""" + hook_directories: list[str] | None = None + """Explicit discovery-only file-hook roots, separate from callback hooks and filesystem + grants. Available without enableConfigDiscovery or enableFileHooks, which controls + repository-origin hooks only; hook/managed disables still apply. Relative SDK paths + resolve once when supplied against the request's effective workingDirectory; retained + roots do not move on cwd changes. Null and omission are equivalent; [] clears only the + explicit SDK contribution. + """ ignored_skills_locations: list[str] | None = None """Skill scan directories and descendants excluded from discovery. Supports `~`-relative paths. @@ -43988,6 +48494,13 @@ class SessionUpdateOptionsParams: """Full set of installed plugins for the session. Replaces the existing list; the runtime invalidates the skills cache only when the list materially changes. """ + instruction_directories: list[str] | None = None + """Explicit discovery-only instruction roots using existing SDK instruction formats, not + filesystem grants. Available without enableConfigDiscovery, subject to + instruction/managed disables. Relative SDK paths resolve once when supplied against the + request's effective workingDirectory; retained roots do not move on cwd changes. Null and + omission are equivalent; [] clears only the explicit SDK contribution. + """ integration_id: str | None = None """Stable integration identifier used for analytics and rate-limit attribution.""" @@ -44061,8 +48574,12 @@ class SessionUpdateOptionsParams: """PowerShell process flags applied to built-in and user-requested shell commands.""" skill_directories: list[str] | None = None - """Additional directories to search for skills.""" - + """Explicit discovery-only skill roots, not filesystem grants. Available without + enableConfigDiscovery, subject to skill/managed disables. Relative SDK paths resolve once + when supplied against the request's effective workingDirectory; retained roots do not + move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK + contribution. + """ skip_custom_instructions: bool | None = None """Whether to skip loading custom instruction sources.""" @@ -44092,6 +48609,7 @@ def from_dict(obj: Any) -> 'SessionUpdateOptionsParams': assert isinstance(obj, dict) additional_content_exclusion_policies = from_union([lambda x: from_list(OptionsUpdateAdditionalContentExclusionPolicy.from_dict, x), from_none], obj.get("additionalContentExclusionPolicies")) agent_context = from_union([from_str, from_none], obj.get("agentContext")) + agent_directories = from_union([lambda x: from_list(from_str, x), from_none], obj.get("agentDirectories")) allow_all_mcp_server_instructions = from_union([from_bool, from_none], obj.get("allowAllMcpServerInstructions")) ask_user_disabled = from_union([from_bool, from_none], obj.get("askUserDisabled")) available_tools = from_union([lambda x: from_list(from_str, x), from_none], obj.get("availableTools")) @@ -44118,10 +48636,12 @@ def from_dict(obj: Any) -> 'SessionUpdateOptionsParams': excluded_builtin_agents = from_union([lambda x: from_list(from_str, x), from_none], obj.get("excludedBuiltinAgents")) excluded_tools = from_union([lambda x: from_list(from_str, x), from_none], obj.get("excludedTools")) feature_flags = from_union([lambda x: from_dict(from_bool, x), from_none], obj.get("featureFlags")) + hook_directories = from_union([lambda x: from_list(from_str, x), from_none], obj.get("hookDirectories")) ignored_skills_locations = from_union([lambda x: from_list(from_str, x), from_none], obj.get("ignoredSkillsLocations")) included_builtin_agents = from_union([lambda x: from_list(from_str, x), from_none], obj.get("includedBuiltinAgents")) included_builtin_skills = from_union([lambda x: from_list(from_str, x), from_none], obj.get("includedBuiltinSkills")) installed_plugins = from_union([lambda x: from_list(SessionInstalledPlugin.from_dict, x), from_none], obj.get("installedPlugins")) + instruction_directories = from_union([lambda x: from_list(from_str, x), from_none], obj.get("instructionDirectories")) integration_id = from_union([from_str, from_none], obj.get("integrationId")) is_experimental_mode = from_union([from_bool, from_none], obj.get("isExperimentalMode")) log_interactive_shells = from_union([from_bool, from_none], obj.get("logInteractiveShells")) @@ -44146,11 +48666,11 @@ def from_dict(obj: Any) -> 'SessionUpdateOptionsParams': skip_custom_instructions = from_union([from_bool, from_none], obj.get("skipCustomInstructions")) skip_embedding_retrieval = from_union([from_bool, from_none], obj.get("skipEmbeddingRetrieval")) suppress_custom_agent_prompt = from_union([from_bool, from_none], obj.get("suppressCustomAgentPrompt")) - tool_filter_precedence = from_union([OptionsUpdateToolFilterPrecedence, from_none], obj.get("toolFilterPrecedence")) + tool_filter_precedence = from_union([from_none, OptionsUpdateToolFilterPrecedence], obj.get("toolFilterPrecedence")) trajectory_file = from_union([from_str, from_none], obj.get("trajectoryFile")) verbosity = from_union([Verbosity, from_none], obj.get("verbosity")) working_directory = from_union([from_str, from_none], obj.get("workingDirectory")) - return SessionUpdateOptionsParams(additional_content_exclusion_policies, agent_context, allow_all_mcp_server_instructions, ask_user_disabled, available_tools, capi, client_name, coauthor_enabled, context_tier, continue_on_auto_mode, copilot_url, custom_agents_local_only, disabled_instruction_sources, disabled_skills, enable_file_hooks, enable_host_git_operations, enable_on_demand_instruction_discovery, enable_reasoning_summaries, enable_script_safety, enable_session_store, enable_skills, enable_streaming, env_value_mode, events_log_directory, events_log_includes_subagents, excluded_builtin_agents, excluded_tools, feature_flags, ignored_skills_locations, included_builtin_agents, included_builtin_skills, installed_plugins, integration_id, is_experimental_mode, log_interactive_shells, lsp_client_name, manage_schedule_enabled, max_inline_binary_bytes, model, model_capabilities_overrides, organization_custom_instructions, provider, reasoning_effort, reasoning_summary, running_in_interactive_mode, sandbox_config, sandbox_config_source, session_capabilities, session_limits, shell, shell_init_profile, shell_process_flags, skill_directories, skip_custom_instructions, skip_embedding_retrieval, suppress_custom_agent_prompt, tool_filter_precedence, trajectory_file, verbosity, working_directory) + return SessionUpdateOptionsParams(additional_content_exclusion_policies, agent_context, agent_directories, allow_all_mcp_server_instructions, ask_user_disabled, available_tools, capi, client_name, coauthor_enabled, context_tier, continue_on_auto_mode, copilot_url, custom_agents_local_only, disabled_instruction_sources, disabled_skills, enable_file_hooks, enable_host_git_operations, enable_on_demand_instruction_discovery, enable_reasoning_summaries, enable_script_safety, enable_session_store, enable_skills, enable_streaming, env_value_mode, events_log_directory, events_log_includes_subagents, excluded_builtin_agents, excluded_tools, feature_flags, hook_directories, ignored_skills_locations, included_builtin_agents, included_builtin_skills, installed_plugins, instruction_directories, integration_id, is_experimental_mode, log_interactive_shells, lsp_client_name, manage_schedule_enabled, max_inline_binary_bytes, model, model_capabilities_overrides, organization_custom_instructions, provider, reasoning_effort, reasoning_summary, running_in_interactive_mode, sandbox_config, sandbox_config_source, session_capabilities, session_limits, shell, shell_init_profile, shell_process_flags, skill_directories, skip_custom_instructions, skip_embedding_retrieval, suppress_custom_agent_prompt, tool_filter_precedence, trajectory_file, verbosity, working_directory) def to_dict(self) -> dict: result: dict = {} @@ -44158,6 +48678,8 @@ def to_dict(self) -> dict: result["additionalContentExclusionPolicies"] = from_union([lambda x: from_list(lambda x: to_class(OptionsUpdateAdditionalContentExclusionPolicy, x), x), from_none], self.additional_content_exclusion_policies) if self.agent_context is not None: result["agentContext"] = from_union([from_str, from_none], self.agent_context) + if self.agent_directories is not None: + result["agentDirectories"] = from_union([lambda x: from_list(from_str, x), from_none], self.agent_directories) if self.allow_all_mcp_server_instructions is not None: result["allowAllMcpServerInstructions"] = from_union([from_bool, from_none], self.allow_all_mcp_server_instructions) if self.ask_user_disabled is not None: @@ -44210,6 +48732,8 @@ def to_dict(self) -> dict: result["excludedTools"] = from_union([lambda x: from_list(from_str, x), from_none], self.excluded_tools) if self.feature_flags is not None: result["featureFlags"] = from_union([lambda x: from_dict(from_bool, x), from_none], self.feature_flags) + if self.hook_directories is not None: + result["hookDirectories"] = from_union([lambda x: from_list(from_str, x), from_none], self.hook_directories) if self.ignored_skills_locations is not None: result["ignoredSkillsLocations"] = from_union([lambda x: from_list(from_str, x), from_none], self.ignored_skills_locations) if self.included_builtin_agents is not None: @@ -44218,6 +48742,8 @@ def to_dict(self) -> dict: result["includedBuiltinSkills"] = from_union([lambda x: from_list(from_str, x), from_none], self.included_builtin_skills) if self.installed_plugins is not None: result["installedPlugins"] = from_union([lambda x: from_list(lambda x: to_class(SessionInstalledPlugin, x), x), from_none], self.installed_plugins) + if self.instruction_directories is not None: + result["instructionDirectories"] = from_union([lambda x: from_list(from_str, x), from_none], self.instruction_directories) if self.integration_id is not None: result["integrationId"] = from_union([from_str, from_none], self.integration_id) if self.is_experimental_mode is not None: @@ -44267,7 +48793,7 @@ def to_dict(self) -> dict: if self.suppress_custom_agent_prompt is not None: result["suppressCustomAgentPrompt"] = from_union([from_bool, from_none], self.suppress_custom_agent_prompt) if self.tool_filter_precedence is not None: - result["toolFilterPrecedence"] = from_union([lambda x: to_enum(OptionsUpdateToolFilterPrecedence, x), from_none], self.tool_filter_precedence) + result["toolFilterPrecedence"] = from_union([from_none, lambda x: to_enum(OptionsUpdateToolFilterPrecedence, x)], self.tool_filter_precedence) if self.trajectory_file is not None: result["trajectoryFile"] = from_union([from_str, from_none], self.trajectory_file) if self.verbosity is not None: @@ -45161,6 +49687,134 @@ def to_dict(self) -> dict: result["token_based_billing"] = from_union([from_bool, from_none], self.token_based_billing) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class AuthIdentity: + """Identity to describe. + + Credential-free authentication identity safe to expose to hosts and user interfaces. + + Identity to evaluate. + + Identity whose host the path resolves against. Omit to resolve against the public + github.com host. + + Credential-free identity metadata for the active account, including resolved Copilot user + information when available. + """ + host: str + """Authentication host""" + + type: AuthInfoType + """Authentication type""" + + copilot_user: CopilotUserResponse | None = None + """Snapshot of the authenticated user's Copilot subscription info, if known""" + + env_var: str | None = None + """Name of the environment variable that supplied the credential, when applicable""" + + login: str | None = None + """Authenticated login, when available""" + + registration_id: str | None = None + """Opaque SDK GitHub credential registration backing this identity. Routing metadata only; + never a credential. + """ + + @staticmethod + def from_dict(obj: Any) -> 'AuthIdentity': + assert isinstance(obj, dict) + host = from_str(obj.get("host")) + type = AuthInfoType(obj.get("type")) + copilot_user = from_union([CopilotUserResponse.from_dict, from_none], obj.get("copilotUser")) + env_var = from_union([from_str, from_none], obj.get("envVar")) + login = from_union([from_str, from_none], obj.get("login")) + registration_id = from_union([from_str, from_none], obj.get("registrationId")) + return AuthIdentity(host, type, copilot_user, env_var, login, registration_id) + + def to_dict(self) -> dict: + result: dict = {} + result["host"] = from_str(self.host) + result["type"] = to_enum(AuthInfoType, self.type) + if self.copilot_user is not None: + result["copilotUser"] = from_union([lambda x: to_class(CopilotUserResponse, x), from_none], self.copilot_user) + if self.env_var is not None: + result["envVar"] = from_union([from_str, from_none], self.env_var) + if self.login is not None: + result["login"] = from_union([from_str, from_none], self.login) + if self.registration_id is not None: + result["registrationId"] = from_union([from_str, from_none], self.registration_id) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _AccountDescribeAuthInfoRequest: + """The authentication identity whose shared wording should be rendered.""" + + identity: AuthIdentity + """Identity to describe.""" + + @staticmethod + def from_dict(obj: Any) -> '_AccountDescribeAuthInfoRequest': + assert isinstance(obj, dict) + identity = AuthIdentity.from_dict(obj.get("identity")) + return _AccountDescribeAuthInfoRequest(identity) + + def to_dict(self) -> dict: + result: dict = {} + result["identity"] = to_class(AuthIdentity, self.identity) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _AccountGetGistSupportRequest: + """The authentication identity whose gist support should be reported.""" + + identity: AuthIdentity + """Identity to evaluate.""" + + @staticmethod + def from_dict(obj: Any) -> '_AccountGetGistSupportRequest': + assert isinstance(obj, dict) + identity = AuthIdentity.from_dict(obj.get("identity")) + return _AccountGetGistSupportRequest(identity) + + def to_dict(self) -> dict: + result: dict = {} + result["identity"] = to_class(AuthIdentity, self.identity) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _AccountGetWebURLRequest: + """A path to resolve against the GitHub host an identity is signed in to.""" + + path: str + """Host-relative path, without a leading slash, for example `settings/copilot/features`.""" + + identity: AuthIdentity | None = None + """Identity whose host the path resolves against. Omit to resolve against the public + github.com host. + """ + + @staticmethod + def from_dict(obj: Any) -> '_AccountGetWebURLRequest': + assert isinstance(obj, dict) + path = from_str(obj.get("path")) + identity = from_union([AuthIdentity.from_dict, from_none], obj.get("identity")) + return _AccountGetWebURLRequest(path, identity) + + def to_dict(self) -> dict: + result: dict = {} + result["path"] = from_str(self.path) + if self.identity is not None: + result["identity"] = from_union([lambda x: to_class(AuthIdentity, x), from_none], self.identity) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class AgentRegistryLiveTargetEntry: @@ -45419,59 +50073,6 @@ def to_dict(self) -> dict: result["copilotUser"] = from_union([lambda x: to_class(CopilotUserResponse, x), from_none], self.copilot_user) return result -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class AuthIdentity: - """Credential-free authentication identity safe to expose to hosts and user interfaces. - - Credential-free identity metadata for the active account, including resolved Copilot user - information when available. - """ - host: str - """Authentication host""" - - type: AuthInfoType - """Authentication type""" - - copilot_user: CopilotUserResponse | None = None - """Snapshot of the authenticated user's Copilot subscription info, if known""" - - env_var: str | None = None - """Name of the environment variable that supplied the credential, when applicable""" - - login: str | None = None - """Authenticated login, when available""" - - registration_id: str | None = None - """Opaque SDK GitHub credential registration backing this identity. Routing metadata only; - never a credential. - """ - - @staticmethod - def from_dict(obj: Any) -> 'AuthIdentity': - assert isinstance(obj, dict) - host = from_str(obj.get("host")) - type = AuthInfoType(obj.get("type")) - copilot_user = from_union([CopilotUserResponse.from_dict, from_none], obj.get("copilotUser")) - env_var = from_union([from_str, from_none], obj.get("envVar")) - login = from_union([from_str, from_none], obj.get("login")) - registration_id = from_union([from_str, from_none], obj.get("registrationId")) - return AuthIdentity(host, type, copilot_user, env_var, login, registration_id) - - def to_dict(self) -> dict: - result: dict = {} - result["host"] = from_str(self.host) - result["type"] = to_enum(AuthInfoType, self.type) - if self.copilot_user is not None: - result["copilotUser"] = from_union([lambda x: to_class(CopilotUserResponse, x), from_none], self.copilot_user) - if self.env_var is not None: - result["envVar"] = from_union([from_str, from_none], self.env_var) - if self.login is not None: - result["login"] = from_union([from_str, from_none], self.login) - if self.registration_id is not None: - result["registrationId"] = from_union([from_str, from_none], self.registration_id) - return result - # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class AuthReadValue: @@ -45577,64 +50178,6 @@ def to_dict(self) -> dict: result["type"] = from_union([from_none, from_str], self.type) return result -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class ConnectorDiscoveryAccountList: - """Eligible accounts.""" - - accounts: list[ConnectorDiscoveryAccount] - """Eligible accounts.""" - - availability: ConnectorDiscoveryAvailability - """Availability.""" - - @staticmethod - def from_dict(obj: Any) -> 'ConnectorDiscoveryAccountList': - assert isinstance(obj, dict) - accounts = from_list(ConnectorDiscoveryAccount.from_dict, obj.get("accounts")) - availability = ConnectorDiscoveryAvailability(obj.get("availability")) - return ConnectorDiscoveryAccountList(accounts, availability) - - def to_dict(self) -> dict: - result: dict = {} - result["accounts"] = from_list(lambda x: to_class(ConnectorDiscoveryAccount, x), self.accounts) - result["availability"] = to_enum(ConnectorDiscoveryAvailability, self.availability) - return result - -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class ConnectorDiscoveryCapabilities: - """Feature availability.""" - - api_version: int - """API version.""" - - availability: ConnectorDiscoveryAvailability - """Availability.""" - - conditional_cache: bool - """Whether results are cached.""" - - opaque_account_selection: bool - """Whether accounts are selected by opaque ID.""" - - @staticmethod - def from_dict(obj: Any) -> 'ConnectorDiscoveryCapabilities': - assert isinstance(obj, dict) - api_version = from_int(obj.get("apiVersion")) - availability = ConnectorDiscoveryAvailability(obj.get("availability")) - conditional_cache = from_bool(obj.get("conditionalCache")) - opaque_account_selection = from_bool(obj.get("opaqueAccountSelection")) - return ConnectorDiscoveryCapabilities(api_version, availability, conditional_cache, opaque_account_selection) - - def to_dict(self) -> dict: - result: dict = {} - result["apiVersion"] = from_int(self.api_version) - result["availability"] = to_enum(ConnectorDiscoveryAvailability, self.availability) - result["conditionalCache"] = from_bool(self.conditional_cache) - result["opaqueAccountSelection"] = from_bool(self.opaque_account_selection) - return result - # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class CopilotAPITokenAuthInfo: @@ -46211,6 +50754,99 @@ def to_dict(self) -> dict: result["copilotUser"] = from_union([lambda x: to_class(CopilotUserResponse, x), from_none], self.copilot_user) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class ManagedPermissionOperation: + """Original input operation associated with this verdict.""" + + kind: ManagedPermissionOperationKind + url: str + """Absolute HTTP(S) URL with a host. Invalid URLs reject the entire evaluation batch.""" + + @staticmethod + def from_dict(obj: Any) -> 'ManagedPermissionOperation': + assert isinstance(obj, dict) + kind = ManagedPermissionOperationKind(obj.get("kind")) + url = from_str(obj.get("url")) + return ManagedPermissionOperation(kind, url) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = to_enum(ManagedPermissionOperationKind, self.kind) + result["url"] = from_str(self.url) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class ManagedPermissionEvaluation: + operation: ManagedPermissionOperation + """Original input operation associated with this verdict.""" + + verdict: ManagedPermissionVerdict + """Managed-policy decision only; never bypasses user permissions or other security controls.""" + + @staticmethod + def from_dict(obj: Any) -> 'ManagedPermissionEvaluation': + assert isinstance(obj, dict) + operation = ManagedPermissionOperation.from_dict(obj.get("operation")) + verdict = ManagedPermissionVerdict(obj.get("verdict")) + return ManagedPermissionEvaluation(operation, verdict) + + def to_dict(self) -> dict: + result: dict = {} + result["operation"] = to_class(ManagedPermissionOperation, self.operation) + result["verdict"] = to_enum(ManagedPermissionVerdict, self.verdict) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class ManagedPermissionsEvaluateRequest: + """Managed policy context and ordered operations for pure, sessionless evaluation.""" + + context: ManagedPermissionsContext + """Trusted, reusable managed permission policy snapshot; no policy is discovered during + evaluation. + """ + operations: list[ManagedPermissionOperation] + """Operations to evaluate, preserving input order and duplicates. An empty batch is valid.""" + + @staticmethod + def from_dict(obj: Any) -> 'ManagedPermissionsEvaluateRequest': + assert isinstance(obj, dict) + context = ManagedPermissionsContext.from_dict(obj.get("context")) + operations = from_list(ManagedPermissionOperation.from_dict, obj.get("operations")) + return ManagedPermissionsEvaluateRequest(context, operations) + + def to_dict(self) -> dict: + result: dict = {} + result["context"] = to_class(ManagedPermissionsContext, self.context) + result["operations"] = from_list(lambda x: to_class(ManagedPermissionOperation, x), self.operations) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class ManagedPermissionsEvaluateResult: + """Ordered managed permission verdicts and the supplied policy's fail-closed posture.""" + + fail_closed: bool + """Managed permission policy could not be determined; every operation is denied.""" + + results: list[ManagedPermissionEvaluation] + """One verdict per input operation, in the same order, including duplicates.""" + + @staticmethod + def from_dict(obj: Any) -> 'ManagedPermissionsEvaluateResult': + assert isinstance(obj, dict) + fail_closed = from_bool(obj.get("failClosed")) + results = from_list(ManagedPermissionEvaluation.from_dict, obj.get("results")) + return ManagedPermissionsEvaluateResult(fail_closed, results) + + def to_dict(self) -> dict: + result: dict = {} + result["failClosed"] = from_bool(self.fail_closed) + result["results"] = from_list(lambda x: to_class(ManagedPermissionEvaluation, x), self.results) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ManagedSettingsSchemaResult: @@ -46354,7 +50990,7 @@ def to_dict(self) -> dict: # Experimental: this type is part of an experimental API and may change or be removed. # Internal: this type is an internal SDK API and is not part of the public surface. @dataclass -class MCPRegisterExternalClientRequest: +class _MCPRegisterExternalClientRequest: """Registration parameters for an external MCP client.""" client: Any @@ -46374,13 +51010,13 @@ class MCPRegisterExternalClientRequest: """ @staticmethod - def from_dict(obj: Any) -> 'MCPRegisterExternalClientRequest': + def from_dict(obj: Any) -> '_MCPRegisterExternalClientRequest': assert isinstance(obj, dict) client = obj.get("client") config = obj.get("config") server_name = from_str(obj.get("serverName")) transport = obj.get("transport") - return MCPRegisterExternalClientRequest(client, config, server_name, transport) + return _MCPRegisterExternalClientRequest(client, config, server_name, transport) def to_dict(self) -> dict: result: dict = {} @@ -46390,6 +51026,57 @@ def to_dict(self) -> dict: result["transport"] = self.transport return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _MCPRegistrySearchRequest: + """Registry search terms, the credential to search under, and the request id that makes the + search cancellable. + """ + auth_info: Any + """The credential the search runs under, carried opaquely. Hosts usually send + credential-free `AuthIdentity`; a `token` identity can be resolved only when it embeds a + token, while `env` and `gh-cli` identities can use a token embedded in the request first. + """ + limit: int + """Maximum number of servers to return.""" + + request_id: int + """Request ID from `mcp.registry.allocateRequestId`. The search refuses unknown, reclaimed, + or canceled IDs and IDs that another search or request already uses. + """ + query: str | None = None + """Free-text query. Omitted or empty asks the registry for its top servers rather than + searching. A value that is not a string is refused. + """ + repository: str | None = None + """Repository used for the policy lookup, as `owner/name`. The policy selects the registry + URL and whether the user token goes to the registry. The registry receives this + repository only when the policy entry lists it as required context. A value that is not a + string is refused. + """ + + @staticmethod + def from_dict(obj: Any) -> '_MCPRegistrySearchRequest': + assert isinstance(obj, dict) + auth_info = obj.get("authInfo") + limit = from_int(obj.get("limit")) + request_id = from_int(obj.get("requestId")) + query = from_union([from_str, from_none], obj.get("query")) + repository = from_union([from_str, from_none], obj.get("repository")) + return _MCPRegistrySearchRequest(auth_info, limit, request_id, query, repository) + + def to_dict(self) -> dict: + result: dict = {} + result["authInfo"] = self.auth_info + result["limit"] = from_int(self.limit) + result["requestId"] = from_int(self.request_id) + if self.query is not None: + result["query"] = from_union([from_str, from_none], self.query) + if self.repository is not None: + result["repository"] = from_union([from_str, from_none], self.repository) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MCPServerConfig: @@ -46639,7 +51326,7 @@ def to_dict(self) -> dict: # Experimental: this type is part of an experimental API and may change or be removed. # Internal: this type is an internal SDK API and is not part of the public surface. @dataclass -class MCPReloadConfig: +class _MCPReloadConfig: """In-process MCP reload configuration.""" mcp_servers: dict[str, MCPServerConfig] @@ -46659,7 +51346,7 @@ class MCPReloadConfig: use_cached_tool_snapshots: bool | None = None @staticmethod - def from_dict(obj: Any) -> 'MCPReloadConfig': + def from_dict(obj: Any) -> '_MCPReloadConfig': assert isinstance(obj, dict) mcp_servers = from_dict(MCPServerConfig.from_dict, obj.get("mcpServers")) active_git_hub_token = from_union([from_str, from_none], obj.get("activeGitHubToken")) @@ -46674,7 +51361,7 @@ def from_dict(obj: Any) -> 'MCPReloadConfig': mcp3_p_enabled = from_union([from_bool, from_none], obj.get("mcp3pEnabled")) secret_store = obj.get("secretStore") use_cached_tool_snapshots = from_union([from_bool, from_none], obj.get("useCachedToolSnapshots")) - return MCPReloadConfig(mcp_servers, active_git_hub_token, cli_enabled_servers, config_filter, disabled_servers, enabled_servers, force_restart, github_mcp_tool_options, github_mcp_user_override, include_workspace_sources, mcp3_p_enabled, secret_store, use_cached_tool_snapshots) + return _MCPReloadConfig(mcp_servers, active_git_hub_token, cli_enabled_servers, config_filter, disabled_servers, enabled_servers, force_restart, github_mcp_tool_options, github_mcp_user_override, include_workspace_sources, mcp3_p_enabled, secret_store, use_cached_tool_snapshots) def to_dict(self) -> dict: result: dict = {} @@ -46943,13 +51630,13 @@ def to_dict(self) -> dict: # Experimental: this type is part of an experimental API and may change or be removed. # Internal: this type is an internal SDK API and is not part of the public surface. @dataclass -class MCPServerConfigMemory: +class _MCPServerConfigMemory: """In-process MCP server configuration used by embedded SDK clients.""" server_instance: Any """In-process MCP server instance. This value cannot cross a JSON-RPC boundary.""" - type: MCPServerConfigMemoryType + type: _MCPServerConfigMemoryType config_warnings: list[str] | None = None """Configuration warnings recorded while loading the server.""" @@ -47013,10 +51700,10 @@ class MCPServerConfigMemory: """Tools to include. Defaults to all tools if not specified.""" @staticmethod - def from_dict(obj: Any) -> 'MCPServerConfigMemory': + def from_dict(obj: Any) -> '_MCPServerConfigMemory': assert isinstance(obj, dict) server_instance = obj.get("serverInstance") - type = MCPServerConfigMemoryType(obj.get("type")) + type = _MCPServerConfigMemoryType(obj.get("type")) config_warnings = from_union([lambda x: from_list(from_str, x), from_none], obj.get("configWarnings")) defer_tools = from_union([MCPServerConfigDeferTools, from_none], obj.get("deferTools")) disable_secret_masking = from_union([from_bool, from_none], obj.get("disableSecretMasking")) @@ -47037,12 +51724,12 @@ def from_dict(obj: Any) -> 'MCPServerConfigMemory': source_plugin_version = from_union([from_str, from_none], obj.get("sourcePluginVersion")) timeout = from_union([from_int, from_none], obj.get("timeout")) tools = from_union([lambda x: from_list(from_str, x), from_none], obj.get("tools")) - return MCPServerConfigMemory(server_instance, type, config_warnings, defer_tools, disable_secret_masking, disable_tool_cache, display_name, events, exclude_tools, filter_mapping, is_default_server, notifications, oidc, safe_for_telemetry, slow_connection_threshold_ms, source, source_path, source_plugin, source_plugin_spec, source_plugin_version, timeout, tools) + return _MCPServerConfigMemory(server_instance, type, config_warnings, defer_tools, disable_secret_masking, disable_tool_cache, display_name, events, exclude_tools, filter_mapping, is_default_server, notifications, oidc, safe_for_telemetry, slow_connection_threshold_ms, source, source_path, source_plugin, source_plugin_spec, source_plugin_version, timeout, tools) def to_dict(self) -> dict: result: dict = {} result["serverInstance"] = self.server_instance - result["type"] = to_enum(MCPServerConfigMemoryType, self.type) + result["type"] = to_enum(_MCPServerConfigMemoryType, self.type) if self.config_warnings is not None: result["configWarnings"] = from_union([lambda x: from_list(from_str, x), from_none], self.config_warnings) if self.defer_tools is not None: @@ -48054,6 +52741,13 @@ class SessionOpenOptions: agent_context: str | None = None """Runtime context discriminator for agent filtering.""" + agent_directories: list[str] | None = None + """Explicit discovery-only agent roots, separate from customAgents and filesystem grants. + Available without enableConfigDiscovery, subject to managed/source disables. Relative SDK + paths resolve once when supplied against the request's effective workingDirectory; + retained roots do not move on cwd changes. Null and omission are equivalent; [] clears + only the explicit SDK contribution. + """ allow_all_mcp_server_instructions: bool | None = None """Whether to include instructions from every MCP server in the system prompt instead of only allowlisted servers. @@ -48154,8 +52848,8 @@ class SessionOpenOptions: """Whether shell-script safety heuristics are enabled.""" enable_skills: bool | None = None - """Whether skill loading is enabled. When omitted, an SDK skill provider enables skills by - default. + """Whether skill loading is enabled. When omitted, explicit SDK skill roots or an SDK skill + provider enable skills independently of enableConfigDiscovery. """ enable_streaming: bool | None = None """Whether model responses stream as delta events.""" @@ -48200,6 +52894,14 @@ class SessionOpenOptions: sessions.open flows reject it because they cannot safely pre-register the callback handler. """ + hook_directories: list[str] | None = None + """Explicit discovery-only file-hook roots, separate from callback hooks and filesystem + grants. Available without enableConfigDiscovery or enableFileHooks, which controls + repository-origin hooks only; hook/managed disables still apply. Relative SDK paths + resolve once when supplied against the request's effective workingDirectory; retained + roots do not move on cwd changes. Null and omission are equivalent; [] clears only the + explicit SDK contribution. + """ ignored_skills_locations: list[str] | None = None """Skill scan directories and descendants excluded from discovery. Supports `~`-relative paths. @@ -48217,6 +52919,13 @@ class SessionOpenOptions: installed_plugins: list[InstalledPlugin] | None = None """Installed plugins visible to the session.""" + instruction_directories: list[str] | None = None + """Explicit discovery-only instruction roots using existing SDK instruction formats, not + filesystem grants. Available without enableConfigDiscovery, subject to + instruction/managed disables. Relative SDK paths resolve once when supplied against the + request's effective workingDirectory; retained roots do not move on cwd changes. Null and + omission are equivalent; [] clears only the explicit SDK contribution. + """ integration_id: str | None = None """Stable integration identifier for analytics.""" @@ -48317,8 +53026,12 @@ class SessionOpenOptions: """PowerShell process flags applied to built-in and user-requested shell commands.""" skill_directories: list[str] | None = None - """Additional directories to search for skills.""" - + """Explicit discovery-only skill roots, not filesystem grants. Available without + enableConfigDiscovery, subject to skill/managed disables. Relative SDK paths resolve once + when supplied against the request's effective workingDirectory; retained roots do not + move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK + contribution. + """ skip_custom_instructions: bool | None = None """Whether to skip custom instruction sources.""" @@ -48340,6 +53053,7 @@ def from_dict(obj: Any) -> 'SessionOpenOptions': additional_content_exclusion_policies = from_union([lambda x: from_list(SessionOpenOptionsAdditionalContentExclusionPolicy.from_dict, x), from_none], obj.get("additionalContentExclusionPolicies")) additional_directories = from_union([lambda x: from_list(from_str, x), from_none], obj.get("additionalDirectories")) agent_context = from_union([from_str, from_none], obj.get("agentContext")) + agent_directories = from_union([lambda x: from_list(from_str, x), from_none], obj.get("agentDirectories")) allow_all_mcp_server_instructions = from_union([from_bool, from_none], obj.get("allowAllMcpServerInstructions")) ask_user_disabled = from_union([from_bool, from_none], obj.get("askUserDisabled")) auth_client_id_metadata_url = from_union([from_str, from_none], obj.get("authClientIdMetadataUrl")) @@ -48375,10 +53089,12 @@ def from_dict(obj: Any) -> 'SessionOpenOptions': exp_assignments = obj.get("expAssignments") feature_flags = from_union([lambda x: from_dict(from_bool, x), from_none], obj.get("featureFlags")) has_skill_provider = from_union([from_bool, from_none], obj.get("hasSkillProvider")) + hook_directories = from_union([lambda x: from_list(from_str, x), from_none], obj.get("hookDirectories")) ignored_skills_locations = from_union([lambda x: from_list(from_str, x), from_none], obj.get("ignoredSkillsLocations")) included_builtin_agents = from_union([lambda x: from_list(from_str, x), from_none], obj.get("includedBuiltinAgents")) included_builtin_skills = from_union([lambda x: from_list(from_str, x), from_none], obj.get("includedBuiltinSkills")) installed_plugins = from_union([lambda x: from_list(InstalledPlugin.from_dict, x), from_none], obj.get("installedPlugins")) + instruction_directories = from_union([lambda x: from_list(from_str, x), from_none], obj.get("instructionDirectories")) integration_id = from_union([from_str, from_none], obj.get("integrationId")) is_experimental_mode = from_union([from_bool, from_none], obj.get("isExperimentalMode")) log_interactive_shells = from_union([from_bool, from_none], obj.get("logInteractiveShells")) @@ -48414,7 +53130,7 @@ def from_dict(obj: Any) -> 'SessionOpenOptions': verbosity = from_union([Verbosity, from_none], obj.get("verbosity")) working_directory = from_union([from_str, from_none], obj.get("workingDirectory")) working_directory_context = from_union([SessionContext.from_dict, from_none], obj.get("workingDirectoryContext")) - return SessionOpenOptions(additional_content_exclusion_policies, additional_directories, agent_context, allow_all_mcp_server_instructions, ask_user_disabled, auth_client_id_metadata_url, auth_info, auto_tier_is_explicit, available_tools, capi, client_kind, client_name, coauthor_enabled, config_dir, continue_on_auto_mode, copilot_url, custom_agents_local_only, detached_from_spawning_parent_engagement_id, detached_from_spawning_parent_session_id, disabled_instruction_sources, disabled_mcp_servers, disabled_skills, enable_citations, enable_file_change_tracking, enable_managed_settings, enable_on_demand_instruction_discovery, enable_script_safety, enable_skills, enable_streaming, enforce_managed_model_defaults, env_value_mode, events_log_directory, events_log_includes_subagents, excluded_builtin_agents, excluded_tools, exp_assignments, feature_flags, has_skill_provider, ignored_skills_locations, included_builtin_agents, included_builtin_skills, installed_plugins, integration_id, is_experimental_mode, log_interactive_shells, lsp_client_name, managed_mcp_servers, managed_settings, max_inline_binary_bytes, memory, model, model_capabilities_overrides, models, name, provider, providers, reasoning_effort, reasoning_summary, refresh_custom_instructions, remote_defaulted_on, remote_exporting, remote_steerable, running_in_interactive_mode, sandbox_config, sandbox_config_source, session_capabilities, session_id, session_limits, shell, shell_init_profile, shell_process_flags, skill_directories, skip_custom_instructions, trajectory_file, verbosity, working_directory, working_directory_context) + return SessionOpenOptions(additional_content_exclusion_policies, additional_directories, agent_context, agent_directories, allow_all_mcp_server_instructions, ask_user_disabled, auth_client_id_metadata_url, auth_info, auto_tier_is_explicit, available_tools, capi, client_kind, client_name, coauthor_enabled, config_dir, continue_on_auto_mode, copilot_url, custom_agents_local_only, detached_from_spawning_parent_engagement_id, detached_from_spawning_parent_session_id, disabled_instruction_sources, disabled_mcp_servers, disabled_skills, enable_citations, enable_file_change_tracking, enable_managed_settings, enable_on_demand_instruction_discovery, enable_script_safety, enable_skills, enable_streaming, enforce_managed_model_defaults, env_value_mode, events_log_directory, events_log_includes_subagents, excluded_builtin_agents, excluded_tools, exp_assignments, feature_flags, has_skill_provider, hook_directories, ignored_skills_locations, included_builtin_agents, included_builtin_skills, installed_plugins, instruction_directories, integration_id, is_experimental_mode, log_interactive_shells, lsp_client_name, managed_mcp_servers, managed_settings, max_inline_binary_bytes, memory, model, model_capabilities_overrides, models, name, provider, providers, reasoning_effort, reasoning_summary, refresh_custom_instructions, remote_defaulted_on, remote_exporting, remote_steerable, running_in_interactive_mode, sandbox_config, sandbox_config_source, session_capabilities, session_id, session_limits, shell, shell_init_profile, shell_process_flags, skill_directories, skip_custom_instructions, trajectory_file, verbosity, working_directory, working_directory_context) def to_dict(self) -> dict: result: dict = {} @@ -48424,6 +53140,8 @@ def to_dict(self) -> dict: result["additionalDirectories"] = from_union([lambda x: from_list(from_str, x), from_none], self.additional_directories) if self.agent_context is not None: result["agentContext"] = from_union([from_str, from_none], self.agent_context) + if self.agent_directories is not None: + result["agentDirectories"] = from_union([lambda x: from_list(from_str, x), from_none], self.agent_directories) if self.allow_all_mcp_server_instructions is not None: result["allowAllMcpServerInstructions"] = from_union([from_bool, from_none], self.allow_all_mcp_server_instructions) if self.ask_user_disabled is not None: @@ -48494,6 +53212,8 @@ def to_dict(self) -> dict: result["featureFlags"] = from_union([lambda x: from_dict(from_bool, x), from_none], self.feature_flags) if self.has_skill_provider is not None: result["hasSkillProvider"] = from_union([from_bool, from_none], self.has_skill_provider) + if self.hook_directories is not None: + result["hookDirectories"] = from_union([lambda x: from_list(from_str, x), from_none], self.hook_directories) if self.ignored_skills_locations is not None: result["ignoredSkillsLocations"] = from_union([lambda x: from_list(from_str, x), from_none], self.ignored_skills_locations) if self.included_builtin_agents is not None: @@ -48502,6 +53222,8 @@ def to_dict(self) -> dict: result["includedBuiltinSkills"] = from_union([lambda x: from_list(from_str, x), from_none], self.included_builtin_skills) if self.installed_plugins is not None: result["installedPlugins"] = from_union([lambda x: from_list(lambda x: to_class(InstalledPlugin, x), x), from_none], self.installed_plugins) + if self.instruction_directories is not None: + result["instructionDirectories"] = from_union([lambda x: from_list(from_str, x), from_none], self.instruction_directories) if self.integration_id is not None: result["integrationId"] = from_union([from_str, from_none], self.integration_id) if self.is_experimental_mode is not None: @@ -49017,6 +53739,40 @@ def to_dict(self) -> dict: result["copilotUser"] = from_union([lambda x: to_class(CopilotUserResponse, x), from_none], self.copilot_user) return result +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +class _SettingsEditStatus(Enum): + """Changed, unchanged or invalid existing file. Rejected input, validation failures and I/O + errors remain RPC errors. + + Whether one atomic settings edit changed a file. + """ + CHANGED = "changed" + INVALID = "invalid" + UNCHANGED = "unchanged" + +# Experimental: this type is part of an experimental API and may change or be removed. +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _SettingsEditResult: + """Outcome of an atomic user or repository settings edit.""" + + status: _SettingsEditStatus + """Changed, unchanged or invalid existing file. Rejected input, validation failures and I/O + errors remain RPC errors. + """ + + @staticmethod + def from_dict(obj: Any) -> '_SettingsEditResult': + assert isinstance(obj, dict) + status = _SettingsEditStatus(obj.get("status")) + return _SettingsEditResult(status) + + def to_dict(self) -> dict: + result: dict = {} + result["status"] = to_enum(_SettingsEditStatus, self.status) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ShellExecRequest: @@ -49674,6 +54430,12 @@ def to_dict(self) -> dict: result["terminal"] = from_union([lambda x: to_class(WorkflowRunTerminal, x), from_none], self.terminal) return result +def rpc_from_dict(s: Any) -> RPC: + return RPC.from_dict(s) + +def rpc_to_dict(x: RPC) -> Any: + return to_class(RPC, x) + @dataclass class RPC: abort_request: AbortRequest @@ -49681,10 +54443,17 @@ class RPC: accepted_enqueue_command_result: AcceptedEnqueueCommandResult account_all_users: AccountAllUsers account_auth_info: AccountAuthInfo + account_describe_auth_info_request: _AccountDescribeAuthInfoRequest + account_describe_auth_info_result: _AccountDescribeAuthInfoResult account_get_all_users_result: list[AccountAllUsers] account_get_current_auth_result: AccountGetCurrentAuthResult + account_get_gist_support_request: _AccountGetGistSupportRequest + account_get_gist_support_result: _AccountGetGistSupportResult account_get_quota_request: AccountGetQuotaRequest account_get_quota_result: AccountGetQuotaResult + account_get_token_env_var_result: _AccountGetTokenEnvVarResult + account_get_web_url_request: _AccountGetWebURLRequest + account_get_web_url_result: _AccountGetWebURLResult account_kind: AccountKind account_login_request: AccountLoginRequest account_login_result: AccountLoginResult @@ -50035,12 +54804,21 @@ class RPC: external_tool_text_result_for_llm_content_shell_exit: ExternalToolTextResultForLlmContentShellExit external_tool_text_result_for_llm_content_terminal: ExternalToolTextResultForLlmContentTerminal external_tool_text_result_for_llm_content_text: ExternalToolTextResultForLlmContentText + feature_flag_availability: _FeatureFlagAvailability + feature_flag_metadata: _FeatureFlagMetadata + feature_flags_metadata_result: _FeatureFlagsMetadataResult + feature_flags_resolve_request: _FeatureFlagsResolveRequest + feature_flags_resolve_result: dict[str, bool] + feature_flags_resolve_startup_gates_request: _FeatureFlagsResolveStartupGatesRequest + feature_flags_resolve_startup_gates_result: dict[str, bool] filter_mapping: dict[str, ContentFilterMode] | ContentFilterMode fleet_start_request: FleetStartRequest fleet_start_result: FleetStartResult folder_trust_add_params: FolderTrustAddParams folder_trust_check_params: FolderTrustCheckParams folder_trust_check_result: FolderTrustCheckResult + folder_trust_is_trusted_params: _FolderTrustIsTrustedParams + force_login_orgs_decision: _ForceLoginOrgsDecision gh_cli_auth_info: GhCLIAuthInfo git_current_branch_remote_result: _GitCurrentBranchRemoteResult git_cwd_request: _GitCwdRequest @@ -50050,7 +54828,7 @@ class RPC: git_hub_owners_cancel_result: _GitHubOwnersCancelResult git_hub_owners_list_request: _GitHubOwnersListRequest git_hub_owners_list_result: _GitHubOwnersListResult - git_hub_owners_request_id_result: GitHubOwnersRequestIDResult + git_hub_owners_request_id_result: _GitHubOwnersRequestIDResult git_hub_repository_at_path_request: _GitHubRepositoryAtPathRequest git_hub_repository_at_path_result: _GitHubRepositoryAtPathResult git_hub_repository_identity: GitHubRepositoryIdentity @@ -50136,9 +54914,11 @@ class RPC: instruction_discovery_path_kind: DebugCollectLogsEntryKind instruction_discovery_path_list: InstructionDiscoveryPathList instruction_discovery_path_location: InstructionLocation + instructions_discover_repo_sources_request: _InstructionsDiscoverRepoSourcesRequest instructions_discover_request: InstructionsDiscoverRequest instructions_get_discovery_paths_request: InstructionsGetDiscoveryPathsRequest instructions_get_sources_result: InstructionsGetSourcesResult + instructions_init_prompt_result: _InstructionsInitPromptResult instruction_source: InstructionSource instruction_source_location: InstructionLocation instruction_source_type: InstructionSourceType @@ -50158,26 +54938,60 @@ class RPC: llm_inference_http_response_start_result: LlmInferenceHTTPResponseStartResult llm_inference_set_provider_result: LlmInferenceSetProviderResult local_session_metadata_value: LocalSessionMetadataValue + location_permissions_cleanup_result: _LocationPermissionsCleanupResult logged_in_user: _LoggedInUser login_provider_kind: LoginProviderKind log_request: LogRequest log_result: LogResult lsp_initialize_request: LspInitializeRequest managed_mcp_server_config: ManagedMCPServerConfig + managed_permission_evaluation: ManagedPermissionEvaluation + managed_permission_operation: ManagedPermissionOperation + managed_permission_operation_kind: ManagedPermissionOperationKind + managed_permissions_evaluate_request: ManagedPermissionsEvaluateRequest + managed_permissions_evaluate_result: ManagedPermissionsEvaluateResult + managed_permission_verdict: ManagedPermissionVerdict + managed_plugin_retry_entry: ManagedPluginRetryEntry + managed_plugin_retry_status: ManagedPluginRetryStatus managed_setting_meta: ManagedSettingMeta + managed_settings_apply_controls_params: _ManagedSettingsApplyControlsParams + managed_settings_apply_controls_result: _ManagedSettingsApplyControlsResult + managed_settings_bypass_disable_source: _ManagedSettingsBypassDisableSource + managed_settings_bypass_policy_resolution: _ManagedSettingsBypassPolicyResolution managed_settings_channel: ManagedSettingsChannel managed_settings_compose_layer: ManagedSettingsComposeLayer managed_settings_compose_request: ManagedSettingsComposeRequest managed_settings_compose_result: ManagedSettingsComposeResult managed_settings_diagnostic: ManagedSettingsDiagnostic managed_settings_diagnostic_severity: ManagedSettingsDiagnosticSeverity + managed_settings_ingest_params: _ManagedSettingsIngestParams + managed_settings_ingest_result: _ManagedSettingsIngestResult managed_settings_layer: ManagedSettingsLayer + managed_settings_load_remote_control_params: _ManagedSettingsLoadRemoteControlParams + managed_settings_load_remote_control_result: _ManagedSettingsLoadRemoteControlResult + managed_settings_log_level: _ManagedSettingsLogLevel + managed_settings_log_message: _ManagedSettingsLogMessage managed_settings_meta: ManagedSettingsMeta + managed_settings_parse_client_value_params: _ManagedSettingsParseClientValueParams + managed_settings_parse_client_value_result: _ManagedSettingsParseClientValueResult + managed_settings_policy_helper_authority: _ManagedSettingsPolicyHelperAuthority + managed_settings_policy_helper_reason: _ManagedSettingsPolicyHelperReason + managed_settings_read_device_result: _ManagedSettingsReadDeviceResult managed_settings_read_result: ManagedSettingsReadResult + managed_settings_resolve_bypass_disable_transition_params: _ManagedSettingsResolveBypassDisableTransitionParams + managed_settings_resolve_bypass_disable_transition_result: _ManagedSettingsResolveBypassDisableTransitionResult managed_settings_resolved_data: ManagedSettingsResolvedData + managed_settings_resolve_device_force_login_orgs_gate_params: _ManagedSettingsResolveDeviceForceLoginOrgsGateParams + managed_settings_resolve_effective_value_params: _ManagedSettingsResolveEffectiveValueParams + managed_settings_resolve_effective_value_result: _ManagedSettingsResolveEffectiveValueResult managed_settings_resolve_request: ManagedSettingsResolveRequest managed_settings_resolve_result: ManagedSettingsResolveResult + managed_settings_resolve_startup_policy_params: _ManagedSettingsResolveStartupPolicyParams + managed_settings_resolve_startup_policy_result: _ManagedSettingsResolveStartupPolicyResult + managed_settings_retain_policy_params: _ManagedSettingsRetainPolicyParams managed_settings_schema_result: ManagedSettingsSchemaResult + managed_settings_server_policy_cache_mode: _ManagedSettingsServerPolicyCacheMode + managed_settings_server_policy_resolution: _ManagedSettingsServerPolicyResolution managed_settings_validate_request: ManagedSettingsValidateRequest managed_settings_validate_result: ManagedSettingsValidateResult managed_settings_values: ManagedSettingsValues @@ -50225,7 +55039,7 @@ class RPC: mcp_configured_server: MCPConfiguredServer mcp_configured_server_list: MCPConfiguredServerList mcp_configured_server_state: MCPConfiguredServerState - mcp_configure_git_hub_request: MCPConfigureGitHubRequest + mcp_configure_git_hub_request: _MCPConfigureGitHubRequest mcp_configure_git_hub_result: MCPConfigureGitHubResult mcp_diagnostic_details: MCPDiagnosticDetails mcp_diagnostic_direction: MCPDiagnosticDirection @@ -50263,6 +55077,7 @@ class RPC: mcp_install_plan: MCPInstallPlan mcp_is_server_running_request: MCPIsServerRunningRequest mcp_is_server_running_result: MCPIsServerRunningResult + mcp_list_builtin_server_names_result: _MCPListBuiltinServerNamesResult mcp_list_tools_request: MCPListToolsRequest mcp_list_tools_result: MCPListToolsResult mcp_oauth_authentication_state_changed_request: MCPOauthAuthenticationStateChangedRequest @@ -50329,9 +55144,14 @@ class RPC: mcp_prompts_get_result: MCPPromptsGetResult mcp_prompts_list_request: MCPPromptsListRequest mcp_prompts_list_result: MCPPromptsListResult - mcp_register_external_client_request: MCPRegisterExternalClientRequest - mcp_reload_config: MCPReloadConfig - mcp_reload_with_config_request: MCPReloadWithConfigRequest + mcp_register_external_client_request: _MCPRegisterExternalClientRequest + mcp_registry_cancel_request: _MCPRegistryCancelRequest + mcp_registry_cancel_result: _MCPRegistryCancelResult + mcp_registry_request_id_result: _MCPRegistryRequestIDResult + mcp_registry_search_request: _MCPRegistrySearchRequest + mcp_registry_search_result: _MCPRegistrySearchResult + mcp_reload_config: _MCPReloadConfig + mcp_reload_with_config_request: _MCPReloadWithConfigRequest mcp_remove_git_hub_result: MCPRemoveGitHubResult mcp_resource: MCPResource mcp_resource_annotations: MCPResourceAnnotations @@ -50364,8 +55184,8 @@ class RPC: mcp_server_config_http: MCPServerConfigHTTP mcp_server_config_http_oauth_grant_type: MCPGrantType mcp_server_config_http_type: MCPServerConfigHTTPType - mcp_server_config_memory: MCPServerConfigMemory - mcp_server_config_memory_type: MCPServerConfigMemoryType + mcp_server_config_memory: _MCPServerConfigMemory + mcp_server_config_memory_type: _MCPServerConfigMemoryType mcp_server_config_stdio: MCPServerConfigStdio mcp_server_config_stdio_type: MCPServerConfigStdioType mcp_server_failure_info: MCPServerFailureInfo @@ -50375,6 +55195,8 @@ class RPC: mcp_set_env_value_mode_details: MCPSetEnvValueModeDetails mcp_set_env_value_mode_params: MCPSetEnvValueModeParams mcp_set_env_value_mode_result: MCPSetEnvValueModeResult + mcp_should_exclude_git_hub_tools_request: _MCPShouldExcludeGitHubToolsRequest + mcp_should_exclude_git_hub_tools_result: _MCPShouldExcludeGitHubToolsResult mcp_source_file: MCPSourceFile mcp_source_plugin: MCPSourcePlugin mcp_source_ref: MCPSourceRef @@ -50386,7 +55208,7 @@ class RPC: mcp_tool_ui: MCPToolUI mcp_tool_ui_visibility: MCPToolUIVisibility mcp_uninstall_plan: MCPUninstallPlan - mcp_unregister_external_client_request: MCPUnregisterExternalClientRequest + mcp_unregister_external_client_request: _MCPUnregisterExternalClientRequest memory_configuration: MemoryConfiguration metadata_context_attribution_result: MetadataContextAttributionResult metadata_context_heaviest_messages_request: MetadataContextHeaviestMessagesRequest @@ -50422,6 +55244,8 @@ class RPC: model_capabilities_supports: ModelCapabilitiesSupports model_clear_startup_seed_request: _ModelClearStartupSeedRequest model_clear_startup_seed_result: _ModelClearStartupSeedResult + model_get_reasoning_summaries_request: ModelGetReasoningSummariesRequest + model_get_reasoning_summaries_result: ModelGetReasoningSummariesResult model_list: ModelList model_list_request: Any model_message: ModelMessage @@ -50557,6 +55381,8 @@ class RPC: permissions_configure_additional_content_exclusion_policy_scope: AdditionalContentExclusionPolicyScope permissions_configure_params: PermissionsConfigureParams permissions_configure_result: PermissionsConfigureResult + permissions_evaluate_managed_policy_params: _PermissionsEvaluateManagedPolicyParams + permissions_evaluate_managed_policy_result: PermissionsEvaluateManagedPolicyResult permissions_folder_trust_add_trusted_result: PermissionsFolderTrustAddTrustedResult permissions_get_mode_request: PermissionsGetModeRequest permissions_get_mode_result: PermissionsGetModeResult @@ -50593,6 +55419,8 @@ class RPC: permissions_urls_set_unrestricted_mode_result: PermissionsUrlsSetUnrestrictedModeResult permission_urls_config: PermissionUrlsConfig permission_urls_set_unrestricted_mode_params: PermissionUrlsSetUnrestrictedModeParams + persistence_write_raw_json_locked_request: PersistenceWriteRawJSONLockedRequest + persistence_write_raw_json_locked_result: PersistenceWriteRawJSONLockedResult ping_request: PingRequest ping_result: PingResult plan_read_result: PlanReadResult @@ -50737,7 +55565,17 @@ class RPC: remote_session_metadata_value: RemoteSessionMetadataValue remote_session_mode: RemoteSessionMode remote_session_repository: RemoteSessionRepository + repo_settings_edit_request: _RepoSettingsEditRequest + repo_settings_edit_scope: _RepoSettingsEditScope + repo_settings_read_raw_request: _RepoSettingsReadRawRequest + repo_settings_read_raw_result: _RepoSettingsReadRawResult + repo_settings_write_all_request: _RepoSettingsWriteAllRequest + repo_settings_write_key_request: _RepoSettingsWriteKeyRequest response_format: ResponseFormat + sandbox_apply_defaults_request: SandboxApplyDefaultsRequest + sandbox_check_enforcement_command_result: bool + sandbox_check_platform_support_result: bool + sandbox_check_windows_host_loopback_support_result: bool sandbox_config: SandboxConfig sandbox_config_auth: SandboxConfigAuth sandbox_config_source: SandboxConfigSource @@ -50749,20 +55587,44 @@ class RPC: sandbox_config_user_policy_network_proxy: SandboxConfigUserPolicyNetworkProxy sandbox_config_user_policy_seatbelt: SandboxConfigUserPolicySeatbelt sandbox_credentials_config: SandboxCredentialsConfig + sandbox_credential_suggestion: SandboxCredentialSuggestion + sandbox_credential_suggestions_result: SandboxCredentialSuggestionsResult sandbox_disable_for_session_request: SandboxDisableForSessionRequest sandbox_disable_for_session_result: SandboxDisableForSessionResult + sandbox_docs_url_result: SandboxDocsURLResult + sandbox_effective_for_optional_params: _SandboxEffectiveForOptionalParams + sandbox_effective_for_optional_result: _SandboxEffectiveForOptionalResult + sandbox_enforcement_command_allowed_params: _SandboxEnforcementCommandAllowedParams sandbox_enforcement_status: SandboxEnforcementStatus + sandbox_filter_picker_candidates_params: _SandboxFilterPickerCandidatesParams + sandbox_filter_picker_candidates_result: _SandboxFilterPickerCandidatesResult + sandbox_get_unsupported_message_result: str + sandbox_get_unsupported_setting_disabled_notice_result: str sandbox_grant_path_for_request_request: SandboxGrantPathForRequestRequest sandbox_grant_path_for_request_result: SandboxGrantPathForRequestResult sandbox_host_capability: SandboxHostCapability sandbox_host_capability_name: str sandbox_host_support: SandboxHostSupport sandbox_masked_env_var: SandboxMaskedEnvVar + sandbox_picker_candidate: _SandboxPickerCandidate + sandbox_prepare_picker_policy_params: _SandboxPreparePickerPolicyParams + sandbox_prepare_picker_policy_result: _SandboxPreparePickerPolicyResult + sandbox_proxy_ca_check_installation_support_result: bool + sandbox_proxy_ca_check_relevance_result: bool sandbox_proxy_ca_create_result: SandboxProxyCACreateResult sandbox_proxy_ca_request: SandboxProxyCARequest sandbox_proxy_ca_state: SandboxProxyCAState sandbox_proxy_ca_status: SandboxProxyCAStatus + sandbox_proxy_secret_get_result: SandboxProxySecretGetResult + sandbox_proxy_secret_location: SandboxProxySecretLocation + sandbox_proxy_secret_save_request: SandboxProxySecretSaveRequest + sandbox_proxy_secret_save_result: SandboxProxySecretSaveResult + sandbox_resolve_floored_config_params: _SandboxResolveFlooredConfigParams + sandbox_resolve_floored_config_result: SandboxResolveFlooredConfigResult sandbox_session_change: SandboxSessionChange + sandbox_settings_apply_defaults_result: dict[str, Any] + sandbox_unsupported_startup_notice_params: _SandboxUnsupportedStartupNoticeParams + sandbox_unsupported_startup_notice_result: _SandboxUnsupportedStartupNoticeResult schedule_add_at_request: ScheduleAddAtRequest schedule_add_cron_request: ScheduleAddCronRequest schedule_add_request: ScheduleAddRequest @@ -50893,6 +55755,8 @@ class RPC: session_plugins_install_request: SessionPluginsInstallRequest session_plugins_marketplaces_refresh_request: SessionPluginsMarketplacesRefreshRequest session_plugins_reload_request: SessionPluginsReloadRequest + session_plugins_retry_managed_request: SessionPluginsRetryManagedRequest + session_plugins_retry_managed_result: SessionPluginsRetryManagedResult session_provider_get_endpoint_request: SessionProviderGetEndpointRequest session_prune_result: SessionPruneResult session_quota_delegate_warning: SessionQuotaDelegateWarning @@ -50989,6 +55853,9 @@ class RPC: session_working_directory_context_with_client: SessionWorkingDirectoryContextWithClient settable_auth_info: SettableAuthInfo settable_token_auth_info: SettableTokenAuthInfo + settings_edit_operation: _SettingsEditOperation + settings_edit_result: _SettingsEditResult + settings_edit_status: _SettingsEditStatus shell_cancel_user_requested_request: ShellCancelUserRequestedRequest shell_credentials: ShellCredentials shell_exec_request: ShellExecRequest @@ -51061,6 +55928,8 @@ class RPC: slash_command_show_dialog_result: SlashCommandShowDialogResult slash_command_text_result: SlashCommandTextResult slash_command_timeline_entry: SlashCommandTimelineEntry + staff_log_level_migration_status: StaffLogLevelMigrationStatus + staff_update_channel_migration_status: StaffUpdateChannelMigrationStatus subagent_settings_entry: SubagentSettingsEntry subagent_settings_entry_context_tier: SubagentSettingsEntryContextTier system_message_block: SystemMessageBlock @@ -51175,8 +56044,55 @@ class RPC: user_auth_info: UserAuthInfo user_requested_shell_command_result: UserRequestedShellCommandResult user_setting_metadata: UserSettingMetadata + user_settings_apply_top_key_request: UserSettingsApplyTopKeyRequest + user_settings_apply_top_key_result: UserSettingsApplyTopKeyResult + user_settings_build_runtime_request: _UserSettingsBuildRuntimeRequest + user_settings_build_runtime_result: dict[str, Any] + user_settings_edit_request: _UserSettingsEditRequest + user_settings_get_paths_request: UserSettingsGetPathsRequest + user_settings_get_paths_result: UserSettingsGetPathsResult user_settings_get_result: UserSettingsGetResult + user_settings_governance_keys_result: UserSettingsGovernanceKeysResult + user_settings_host_context: UserSettingsHostContext + user_settings_load_and_merge_repo_request: _UserSettingsLoadAndMergeRepoRequest + user_settings_load_and_merge_repo_result: _UserSettingsLoadAndMergeRepoResult + user_settings_load_for_config_dir_request: _UserSettingsLoadForConfigDirRequest + user_settings_load_for_config_dir_result: dict[str, Any] + user_settings_load_for_edit_result: UserSettingsLoadForEditResult + user_settings_load_repo_request: UserSettingsLoadRepoRequest + user_settings_load_repo_result: UserSettingsLoadRepoResult + user_settings_load_with_warning_result: UserSettingsLoadWithWarningResult + user_settings_merge_managed_and_user_params: _UserSettingsMergeManagedAndUserParams + user_settings_merge_managed_and_user_result: UserSettingsMergeManagedAndUserResult + user_settings_merge_repo_value_params: _UserSettingsMergeRepoValueParams + user_settings_merge_repo_value_result: UserSettingsMergeRepoValueResult + user_settings_migrate_obsolete_on_disk_request: _UserSettingsMigrateObsoleteOnDiskRequest + user_settings_migrate_obsolete_on_disk_result: _UserSettingsMigrateObsoleteOnDiskResult + user_settings_migrate_obsolete_on_disk_status: _UserSettingsMigrateObsoleteOnDiskStatus + user_settings_migrate_staff_log_level_once_request: _UserSettingsMigrateStaffLogLevelOnceRequest + user_settings_migrate_staff_log_level_once_result: _UserSettingsMigrateStaffLogLevelOnceResult + user_settings_migrate_staff_update_channel_once_result: _UserSettingsMigrateStaffUpdateChannelOnceResult + user_settings_normalize_request: UserSettingsNormalizeRequest + user_settings_normalize_result: UserSettingsNormalizeResult + user_settings_path_result: UserSettingsPathResult + user_settings_problem: UserSettingsProblem + user_settings_problem_location: UserSettingsProblemLocation + user_settings_problem_source: UserSettingsProblemSource + user_settings_problems_request: UserSettingsProblemsRequest + user_settings_problems_result: UserSettingsProblemsResult + user_settings_reset_staff_model_selection_once_request: _UserSettingsResetStaffModelSelectionOnceRequest + user_settings_reset_staff_model_selection_once_result: _UserSettingsResetStaffModelSelectionOnceResult + user_settings_set_hook_disabled_request: _UserSettingsSetHookDisabledRequest + user_settings_set_hook_disabled_result: _UserSettingsSetHookDisabledResult user_settings_set_request: UserSettingsSetRequest + user_settings_unknown_keys_warning_request: _UserSettingsUnknownKeysWarningRequest + user_settings_unknown_keys_warning_result: _UserSettingsUnknownKeysWarningResult + user_settings_update_request: _UserSettingsUpdateRequest + user_settings_update_result: _UserSettingsUpdateResult + user_settings_validate_request: UserSettingsValidateRequest + user_settings_validate_result: UserSettingsValidateResult + user_settings_validation_issue: UserSettingsValidationIssue + user_settings_write_all_request: UserSettingsWriteAllRequest visibility_get_result: VisibilityGetResult visibility_set_request: VisibilitySetRequest visibility_set_result: VisibilitySetResult @@ -51277,10 +56193,17 @@ def from_dict(obj: Any) -> 'RPC': accepted_enqueue_command_result = AcceptedEnqueueCommandResult.from_dict(obj.get("AcceptedEnqueueCommandResult")) account_all_users = AccountAllUsers.from_dict(obj.get("AccountAllUsers")) account_auth_info = AccountAuthInfo.from_dict(obj.get("AccountAuthInfo")) + account_describe_auth_info_request = _AccountDescribeAuthInfoRequest.from_dict(obj.get("AccountDescribeAuthInfoRequest")) + account_describe_auth_info_result = _AccountDescribeAuthInfoResult.from_dict(obj.get("AccountDescribeAuthInfoResult")) account_get_all_users_result = from_list(AccountAllUsers.from_dict, obj.get("AccountGetAllUsersResult")) account_get_current_auth_result = AccountGetCurrentAuthResult.from_dict(obj.get("AccountGetCurrentAuthResult")) + account_get_gist_support_request = _AccountGetGistSupportRequest.from_dict(obj.get("AccountGetGistSupportRequest")) + account_get_gist_support_result = _AccountGetGistSupportResult.from_dict(obj.get("AccountGetGistSupportResult")) account_get_quota_request = AccountGetQuotaRequest.from_dict(obj.get("AccountGetQuotaRequest")) account_get_quota_result = AccountGetQuotaResult.from_dict(obj.get("AccountGetQuotaResult")) + account_get_token_env_var_result = _AccountGetTokenEnvVarResult.from_dict(obj.get("AccountGetTokenEnvVarResult")) + account_get_web_url_request = _AccountGetWebURLRequest.from_dict(obj.get("AccountGetWebUrlRequest")) + account_get_web_url_result = _AccountGetWebURLResult.from_dict(obj.get("AccountGetWebUrlResult")) account_kind = AccountKind(obj.get("AccountKind")) account_login_request = AccountLoginRequest.from_dict(obj.get("AccountLoginRequest")) account_login_result = AccountLoginResult.from_dict(obj.get("AccountLoginResult")) @@ -51631,12 +56554,21 @@ def from_dict(obj: Any) -> 'RPC': external_tool_text_result_for_llm_content_shell_exit = ExternalToolTextResultForLlmContentShellExit.from_dict(obj.get("ExternalToolTextResultForLlmContentShellExit")) external_tool_text_result_for_llm_content_terminal = ExternalToolTextResultForLlmContentTerminal.from_dict(obj.get("ExternalToolTextResultForLlmContentTerminal")) external_tool_text_result_for_llm_content_text = ExternalToolTextResultForLlmContentText.from_dict(obj.get("ExternalToolTextResultForLlmContentText")) + feature_flag_availability = _FeatureFlagAvailability(obj.get("FeatureFlagAvailability")) + feature_flag_metadata = _FeatureFlagMetadata.from_dict(obj.get("FeatureFlagMetadata")) + feature_flags_metadata_result = _FeatureFlagsMetadataResult.from_dict(obj.get("FeatureFlagsMetadataResult")) + feature_flags_resolve_request = _FeatureFlagsResolveRequest.from_dict(obj.get("FeatureFlagsResolveRequest")) + feature_flags_resolve_result = from_dict(from_bool, obj.get("FeatureFlagsResolveResult")) + feature_flags_resolve_startup_gates_request = _FeatureFlagsResolveStartupGatesRequest.from_dict(obj.get("FeatureFlagsResolveStartupGatesRequest")) + feature_flags_resolve_startup_gates_result = from_dict(from_bool, obj.get("FeatureFlagsResolveStartupGatesResult")) filter_mapping = from_union([lambda x: from_dict(ContentFilterMode, x), ContentFilterMode], obj.get("FilterMapping")) fleet_start_request = FleetStartRequest.from_dict(obj.get("FleetStartRequest")) fleet_start_result = FleetStartResult.from_dict(obj.get("FleetStartResult")) folder_trust_add_params = FolderTrustAddParams.from_dict(obj.get("FolderTrustAddParams")) folder_trust_check_params = FolderTrustCheckParams.from_dict(obj.get("FolderTrustCheckParams")) folder_trust_check_result = FolderTrustCheckResult.from_dict(obj.get("FolderTrustCheckResult")) + folder_trust_is_trusted_params = _FolderTrustIsTrustedParams.from_dict(obj.get("FolderTrustIsTrustedParams")) + force_login_orgs_decision = _ForceLoginOrgsDecision.from_dict(obj.get("ForceLoginOrgsDecision")) gh_cli_auth_info = GhCLIAuthInfo.from_dict(obj.get("GhCliAuthInfo")) git_current_branch_remote_result = _GitCurrentBranchRemoteResult.from_dict(obj.get("GitCurrentBranchRemoteResult")) git_cwd_request = _GitCwdRequest.from_dict(obj.get("GitCwdRequest")) @@ -51646,7 +56578,7 @@ def from_dict(obj: Any) -> 'RPC': git_hub_owners_cancel_result = _GitHubOwnersCancelResult.from_dict(obj.get("GitHubOwnersCancelResult")) git_hub_owners_list_request = _GitHubOwnersListRequest.from_dict(obj.get("GitHubOwnersListRequest")) git_hub_owners_list_result = _GitHubOwnersListResult.from_dict(obj.get("GitHubOwnersListResult")) - git_hub_owners_request_id_result = GitHubOwnersRequestIDResult.from_dict(obj.get("GitHubOwnersRequestIdResult")) + git_hub_owners_request_id_result = _GitHubOwnersRequestIDResult.from_dict(obj.get("GitHubOwnersRequestIdResult")) git_hub_repository_at_path_request = _GitHubRepositoryAtPathRequest.from_dict(obj.get("GitHubRepositoryAtPathRequest")) git_hub_repository_at_path_result = _GitHubRepositoryAtPathResult.from_dict(obj.get("GitHubRepositoryAtPathResult")) git_hub_repository_identity = GitHubRepositoryIdentity.from_dict(obj.get("GitHubRepositoryIdentity")) @@ -51732,9 +56664,11 @@ def from_dict(obj: Any) -> 'RPC': instruction_discovery_path_kind = DebugCollectLogsEntryKind(obj.get("InstructionDiscoveryPathKind")) instruction_discovery_path_list = InstructionDiscoveryPathList.from_dict(obj.get("InstructionDiscoveryPathList")) instruction_discovery_path_location = InstructionLocation(obj.get("InstructionDiscoveryPathLocation")) + instructions_discover_repo_sources_request = _InstructionsDiscoverRepoSourcesRequest.from_dict(obj.get("InstructionsDiscoverRepoSourcesRequest")) instructions_discover_request = InstructionsDiscoverRequest.from_dict(obj.get("InstructionsDiscoverRequest")) instructions_get_discovery_paths_request = InstructionsGetDiscoveryPathsRequest.from_dict(obj.get("InstructionsGetDiscoveryPathsRequest")) instructions_get_sources_result = InstructionsGetSourcesResult.from_dict(obj.get("InstructionsGetSourcesResult")) + instructions_init_prompt_result = _InstructionsInitPromptResult.from_dict(obj.get("InstructionsInitPromptResult")) instruction_source = InstructionSource.from_dict(obj.get("InstructionSource")) instruction_source_location = InstructionLocation(obj.get("InstructionSourceLocation")) instruction_source_type = InstructionSourceType(obj.get("InstructionSourceType")) @@ -51754,26 +56688,60 @@ def from_dict(obj: Any) -> 'RPC': llm_inference_http_response_start_result = LlmInferenceHTTPResponseStartResult.from_dict(obj.get("LlmInferenceHttpResponseStartResult")) llm_inference_set_provider_result = LlmInferenceSetProviderResult.from_dict(obj.get("LlmInferenceSetProviderResult")) local_session_metadata_value = LocalSessionMetadataValue.from_dict(obj.get("LocalSessionMetadataValue")) + location_permissions_cleanup_result = _LocationPermissionsCleanupResult.from_dict(obj.get("LocationPermissionsCleanupResult")) logged_in_user = _LoggedInUser.from_dict(obj.get("LoggedInUser")) login_provider_kind = LoginProviderKind(obj.get("LoginProviderKind")) log_request = LogRequest.from_dict(obj.get("LogRequest")) log_result = LogResult.from_dict(obj.get("LogResult")) lsp_initialize_request = LspInitializeRequest.from_dict(obj.get("LspInitializeRequest")) managed_mcp_server_config = ManagedMCPServerConfig.from_dict(obj.get("ManagedMcpServerConfig")) + managed_permission_evaluation = ManagedPermissionEvaluation.from_dict(obj.get("ManagedPermissionEvaluation")) + managed_permission_operation = ManagedPermissionOperation.from_dict(obj.get("ManagedPermissionOperation")) + managed_permission_operation_kind = ManagedPermissionOperationKind(obj.get("ManagedPermissionOperationKind")) + managed_permissions_evaluate_request = ManagedPermissionsEvaluateRequest.from_dict(obj.get("ManagedPermissionsEvaluateRequest")) + managed_permissions_evaluate_result = ManagedPermissionsEvaluateResult.from_dict(obj.get("ManagedPermissionsEvaluateResult")) + managed_permission_verdict = ManagedPermissionVerdict(obj.get("ManagedPermissionVerdict")) + managed_plugin_retry_entry = ManagedPluginRetryEntry.from_dict(obj.get("ManagedPluginRetryEntry")) + managed_plugin_retry_status = ManagedPluginRetryStatus(obj.get("ManagedPluginRetryStatus")) managed_setting_meta = ManagedSettingMeta.from_dict(obj.get("ManagedSettingMeta")) + managed_settings_apply_controls_params = _ManagedSettingsApplyControlsParams.from_dict(obj.get("ManagedSettingsApplyControlsParams")) + managed_settings_apply_controls_result = _ManagedSettingsApplyControlsResult.from_dict(obj.get("ManagedSettingsApplyControlsResult")) + managed_settings_bypass_disable_source = _ManagedSettingsBypassDisableSource(obj.get("ManagedSettingsBypassDisableSource")) + managed_settings_bypass_policy_resolution = _ManagedSettingsBypassPolicyResolution(obj.get("ManagedSettingsBypassPolicyResolution")) managed_settings_channel = ManagedSettingsChannel(obj.get("ManagedSettingsChannel")) managed_settings_compose_layer = ManagedSettingsComposeLayer.from_dict(obj.get("ManagedSettingsComposeLayer")) managed_settings_compose_request = ManagedSettingsComposeRequest.from_dict(obj.get("ManagedSettingsComposeRequest")) managed_settings_compose_result = ManagedSettingsComposeResult.from_dict(obj.get("ManagedSettingsComposeResult")) managed_settings_diagnostic = ManagedSettingsDiagnostic.from_dict(obj.get("ManagedSettingsDiagnostic")) managed_settings_diagnostic_severity = ManagedSettingsDiagnosticSeverity(obj.get("ManagedSettingsDiagnosticSeverity")) + managed_settings_ingest_params = _ManagedSettingsIngestParams.from_dict(obj.get("ManagedSettingsIngestParams")) + managed_settings_ingest_result = _ManagedSettingsIngestResult.from_dict(obj.get("ManagedSettingsIngestResult")) managed_settings_layer = ManagedSettingsLayer.from_dict(obj.get("ManagedSettingsLayer")) + managed_settings_load_remote_control_params = _ManagedSettingsLoadRemoteControlParams.from_dict(obj.get("ManagedSettingsLoadRemoteControlParams")) + managed_settings_load_remote_control_result = _ManagedSettingsLoadRemoteControlResult.from_dict(obj.get("ManagedSettingsLoadRemoteControlResult")) + managed_settings_log_level = _ManagedSettingsLogLevel(obj.get("ManagedSettingsLogLevel")) + managed_settings_log_message = _ManagedSettingsLogMessage.from_dict(obj.get("ManagedSettingsLogMessage")) managed_settings_meta = ManagedSettingsMeta.from_dict(obj.get("ManagedSettingsMeta")) + managed_settings_parse_client_value_params = _ManagedSettingsParseClientValueParams.from_dict(obj.get("ManagedSettingsParseClientValueParams")) + managed_settings_parse_client_value_result = _ManagedSettingsParseClientValueResult.from_dict(obj.get("ManagedSettingsParseClientValueResult")) + managed_settings_policy_helper_authority = _ManagedSettingsPolicyHelperAuthority(obj.get("ManagedSettingsPolicyHelperAuthority")) + managed_settings_policy_helper_reason = _ManagedSettingsPolicyHelperReason(obj.get("ManagedSettingsPolicyHelperReason")) + managed_settings_read_device_result = _ManagedSettingsReadDeviceResult.from_dict(obj.get("ManagedSettingsReadDeviceResult")) managed_settings_read_result = ManagedSettingsReadResult.from_dict(obj.get("ManagedSettingsReadResult")) + managed_settings_resolve_bypass_disable_transition_params = _ManagedSettingsResolveBypassDisableTransitionParams.from_dict(obj.get("ManagedSettingsResolveBypassDisableTransitionParams")) + managed_settings_resolve_bypass_disable_transition_result = _ManagedSettingsResolveBypassDisableTransitionResult.from_dict(obj.get("ManagedSettingsResolveBypassDisableTransitionResult")) managed_settings_resolved_data = ManagedSettingsResolvedData.from_dict(obj.get("ManagedSettingsResolvedData")) + managed_settings_resolve_device_force_login_orgs_gate_params = _ManagedSettingsResolveDeviceForceLoginOrgsGateParams.from_dict(obj.get("ManagedSettingsResolveDeviceForceLoginOrgsGateParams")) + managed_settings_resolve_effective_value_params = _ManagedSettingsResolveEffectiveValueParams.from_dict(obj.get("ManagedSettingsResolveEffectiveValueParams")) + managed_settings_resolve_effective_value_result = _ManagedSettingsResolveEffectiveValueResult.from_dict(obj.get("ManagedSettingsResolveEffectiveValueResult")) managed_settings_resolve_request = ManagedSettingsResolveRequest.from_dict(obj.get("ManagedSettingsResolveRequest")) managed_settings_resolve_result = ManagedSettingsResolveResult.from_dict(obj.get("ManagedSettingsResolveResult")) + managed_settings_resolve_startup_policy_params = _ManagedSettingsResolveStartupPolicyParams.from_dict(obj.get("ManagedSettingsResolveStartupPolicyParams")) + managed_settings_resolve_startup_policy_result = _ManagedSettingsResolveStartupPolicyResult.from_dict(obj.get("ManagedSettingsResolveStartupPolicyResult")) + managed_settings_retain_policy_params = _ManagedSettingsRetainPolicyParams.from_dict(obj.get("ManagedSettingsRetainPolicyParams")) managed_settings_schema_result = ManagedSettingsSchemaResult.from_dict(obj.get("ManagedSettingsSchemaResult")) + managed_settings_server_policy_cache_mode = _ManagedSettingsServerPolicyCacheMode(obj.get("ManagedSettingsServerPolicyCacheMode")) + managed_settings_server_policy_resolution = _ManagedSettingsServerPolicyResolution(obj.get("ManagedSettingsServerPolicyResolution")) managed_settings_validate_request = ManagedSettingsValidateRequest.from_dict(obj.get("ManagedSettingsValidateRequest")) managed_settings_validate_result = ManagedSettingsValidateResult.from_dict(obj.get("ManagedSettingsValidateResult")) managed_settings_values = ManagedSettingsValues.from_dict(obj.get("ManagedSettingsValues")) @@ -51821,7 +56789,7 @@ def from_dict(obj: Any) -> 'RPC': mcp_configured_server = MCPConfiguredServer.from_dict(obj.get("McpConfiguredServer")) mcp_configured_server_list = MCPConfiguredServerList.from_dict(obj.get("McpConfiguredServerList")) mcp_configured_server_state = MCPConfiguredServerState.from_dict(obj.get("McpConfiguredServerState")) - mcp_configure_git_hub_request = MCPConfigureGitHubRequest.from_dict(obj.get("McpConfigureGitHubRequest")) + mcp_configure_git_hub_request = _MCPConfigureGitHubRequest.from_dict(obj.get("McpConfigureGitHubRequest")) mcp_configure_git_hub_result = MCPConfigureGitHubResult.from_dict(obj.get("McpConfigureGitHubResult")) mcp_diagnostic_details = MCPDiagnosticDetails.from_dict(obj.get("McpDiagnosticDetails")) mcp_diagnostic_direction = MCPDiagnosticDirection(obj.get("McpDiagnosticDirection")) @@ -51859,6 +56827,7 @@ def from_dict(obj: Any) -> 'RPC': mcp_install_plan = MCPInstallPlan.from_dict(obj.get("McpInstallPlan")) mcp_is_server_running_request = MCPIsServerRunningRequest.from_dict(obj.get("McpIsServerRunningRequest")) mcp_is_server_running_result = MCPIsServerRunningResult.from_dict(obj.get("McpIsServerRunningResult")) + mcp_list_builtin_server_names_result = _MCPListBuiltinServerNamesResult.from_dict(obj.get("McpListBuiltinServerNamesResult")) mcp_list_tools_request = MCPListToolsRequest.from_dict(obj.get("McpListToolsRequest")) mcp_list_tools_result = MCPListToolsResult.from_dict(obj.get("McpListToolsResult")) mcp_oauth_authentication_state_changed_request = MCPOauthAuthenticationStateChangedRequest.from_dict(obj.get("McpOauthAuthenticationStateChangedRequest")) @@ -51925,9 +56894,14 @@ def from_dict(obj: Any) -> 'RPC': mcp_prompts_get_result = MCPPromptsGetResult.from_dict(obj.get("McpPromptsGetResult")) mcp_prompts_list_request = MCPPromptsListRequest.from_dict(obj.get("McpPromptsListRequest")) mcp_prompts_list_result = MCPPromptsListResult.from_dict(obj.get("McpPromptsListResult")) - mcp_register_external_client_request = MCPRegisterExternalClientRequest.from_dict(obj.get("McpRegisterExternalClientRequest")) - mcp_reload_config = MCPReloadConfig.from_dict(obj.get("McpReloadConfig")) - mcp_reload_with_config_request = MCPReloadWithConfigRequest.from_dict(obj.get("McpReloadWithConfigRequest")) + mcp_register_external_client_request = _MCPRegisterExternalClientRequest.from_dict(obj.get("McpRegisterExternalClientRequest")) + mcp_registry_cancel_request = _MCPRegistryCancelRequest.from_dict(obj.get("McpRegistryCancelRequest")) + mcp_registry_cancel_result = _MCPRegistryCancelResult.from_dict(obj.get("McpRegistryCancelResult")) + mcp_registry_request_id_result = _MCPRegistryRequestIDResult.from_dict(obj.get("McpRegistryRequestIdResult")) + mcp_registry_search_request = _MCPRegistrySearchRequest.from_dict(obj.get("McpRegistrySearchRequest")) + mcp_registry_search_result = _MCPRegistrySearchResult.from_dict(obj.get("McpRegistrySearchResult")) + mcp_reload_config = _MCPReloadConfig.from_dict(obj.get("McpReloadConfig")) + mcp_reload_with_config_request = _MCPReloadWithConfigRequest.from_dict(obj.get("McpReloadWithConfigRequest")) mcp_remove_git_hub_result = MCPRemoveGitHubResult.from_dict(obj.get("McpRemoveGitHubResult")) mcp_resource = MCPResource.from_dict(obj.get("McpResource")) mcp_resource_annotations = MCPResourceAnnotations.from_dict(obj.get("McpResourceAnnotations")) @@ -51960,8 +56934,8 @@ def from_dict(obj: Any) -> 'RPC': mcp_server_config_http = MCPServerConfigHTTP.from_dict(obj.get("McpServerConfigHttp")) mcp_server_config_http_oauth_grant_type = MCPGrantType(obj.get("McpServerConfigHttpOauthGrantType")) mcp_server_config_http_type = MCPServerConfigHTTPType(obj.get("McpServerConfigHttpType")) - mcp_server_config_memory = MCPServerConfigMemory.from_dict(obj.get("McpServerConfigMemory")) - mcp_server_config_memory_type = MCPServerConfigMemoryType(obj.get("McpServerConfigMemoryType")) + mcp_server_config_memory = _MCPServerConfigMemory.from_dict(obj.get("McpServerConfigMemory")) + mcp_server_config_memory_type = _MCPServerConfigMemoryType(obj.get("McpServerConfigMemoryType")) mcp_server_config_stdio = MCPServerConfigStdio.from_dict(obj.get("McpServerConfigStdio")) mcp_server_config_stdio_type = MCPServerConfigStdioType(obj.get("McpServerConfigStdioType")) mcp_server_failure_info = MCPServerFailureInfo.from_dict(obj.get("McpServerFailureInfo")) @@ -51971,6 +56945,8 @@ def from_dict(obj: Any) -> 'RPC': mcp_set_env_value_mode_details = MCPSetEnvValueModeDetails(obj.get("McpSetEnvValueModeDetails")) mcp_set_env_value_mode_params = MCPSetEnvValueModeParams.from_dict(obj.get("McpSetEnvValueModeParams")) mcp_set_env_value_mode_result = MCPSetEnvValueModeResult.from_dict(obj.get("McpSetEnvValueModeResult")) + mcp_should_exclude_git_hub_tools_request = _MCPShouldExcludeGitHubToolsRequest.from_dict(obj.get("McpShouldExcludeGitHubToolsRequest")) + mcp_should_exclude_git_hub_tools_result = _MCPShouldExcludeGitHubToolsResult.from_dict(obj.get("McpShouldExcludeGitHubToolsResult")) mcp_source_file = MCPSourceFile.from_dict(obj.get("McpSourceFile")) mcp_source_plugin = MCPSourcePlugin.from_dict(obj.get("McpSourcePlugin")) mcp_source_ref = MCPSourceRef.from_dict(obj.get("McpSourceRef")) @@ -51982,7 +56958,7 @@ def from_dict(obj: Any) -> 'RPC': mcp_tool_ui = MCPToolUI.from_dict(obj.get("McpToolUi")) mcp_tool_ui_visibility = MCPToolUIVisibility(obj.get("McpToolUiVisibility")) mcp_uninstall_plan = MCPUninstallPlan.from_dict(obj.get("McpUninstallPlan")) - mcp_unregister_external_client_request = MCPUnregisterExternalClientRequest.from_dict(obj.get("McpUnregisterExternalClientRequest")) + mcp_unregister_external_client_request = _MCPUnregisterExternalClientRequest.from_dict(obj.get("McpUnregisterExternalClientRequest")) memory_configuration = MemoryConfiguration.from_dict(obj.get("MemoryConfiguration")) metadata_context_attribution_result = MetadataContextAttributionResult.from_dict(obj.get("MetadataContextAttributionResult")) metadata_context_heaviest_messages_request = MetadataContextHeaviestMessagesRequest.from_dict(obj.get("MetadataContextHeaviestMessagesRequest")) @@ -52018,6 +56994,8 @@ def from_dict(obj: Any) -> 'RPC': model_capabilities_supports = ModelCapabilitiesSupports.from_dict(obj.get("ModelCapabilitiesSupports")) model_clear_startup_seed_request = _ModelClearStartupSeedRequest.from_dict(obj.get("ModelClearStartupSeedRequest")) model_clear_startup_seed_result = _ModelClearStartupSeedResult.from_dict(obj.get("ModelClearStartupSeedResult")) + model_get_reasoning_summaries_request = ModelGetReasoningSummariesRequest.from_dict(obj.get("ModelGetReasoningSummariesRequest")) + model_get_reasoning_summaries_result = ModelGetReasoningSummariesResult.from_dict(obj.get("ModelGetReasoningSummariesResult")) model_list = ModelList.from_dict(obj.get("ModelList")) model_list_request = obj.get("ModelListRequest") model_message = ModelMessage.from_dict(obj.get("ModelMessage")) @@ -52153,6 +57131,8 @@ def from_dict(obj: Any) -> 'RPC': permissions_configure_additional_content_exclusion_policy_scope = AdditionalContentExclusionPolicyScope(obj.get("PermissionsConfigureAdditionalContentExclusionPolicyScope")) permissions_configure_params = PermissionsConfigureParams.from_dict(obj.get("PermissionsConfigureParams")) permissions_configure_result = PermissionsConfigureResult.from_dict(obj.get("PermissionsConfigureResult")) + permissions_evaluate_managed_policy_params = _PermissionsEvaluateManagedPolicyParams.from_dict(obj.get("PermissionsEvaluateManagedPolicyParams")) + permissions_evaluate_managed_policy_result = PermissionsEvaluateManagedPolicyResult.from_dict(obj.get("PermissionsEvaluateManagedPolicyResult")) permissions_folder_trust_add_trusted_result = PermissionsFolderTrustAddTrustedResult.from_dict(obj.get("PermissionsFolderTrustAddTrustedResult")) permissions_get_mode_request = PermissionsGetModeRequest.from_dict(obj.get("PermissionsGetModeRequest")) permissions_get_mode_result = PermissionsGetModeResult.from_dict(obj.get("PermissionsGetModeResult")) @@ -52189,6 +57169,8 @@ def from_dict(obj: Any) -> 'RPC': permissions_urls_set_unrestricted_mode_result = PermissionsUrlsSetUnrestrictedModeResult.from_dict(obj.get("PermissionsUrlsSetUnrestrictedModeResult")) permission_urls_config = PermissionUrlsConfig.from_dict(obj.get("PermissionUrlsConfig")) permission_urls_set_unrestricted_mode_params = PermissionUrlsSetUnrestrictedModeParams.from_dict(obj.get("PermissionUrlsSetUnrestrictedModeParams")) + persistence_write_raw_json_locked_request = PersistenceWriteRawJSONLockedRequest.from_dict(obj.get("PersistenceWriteRawJsonLockedRequest")) + persistence_write_raw_json_locked_result = PersistenceWriteRawJSONLockedResult.from_dict(obj.get("PersistenceWriteRawJsonLockedResult")) ping_request = PingRequest.from_dict(obj.get("PingRequest")) ping_result = PingResult.from_dict(obj.get("PingResult")) plan_read_result = PlanReadResult.from_dict(obj.get("PlanReadResult")) @@ -52333,7 +57315,17 @@ def from_dict(obj: Any) -> 'RPC': remote_session_metadata_value = RemoteSessionMetadataValue.from_dict(obj.get("RemoteSessionMetadataValue")) remote_session_mode = RemoteSessionMode(obj.get("RemoteSessionMode")) remote_session_repository = RemoteSessionRepository.from_dict(obj.get("RemoteSessionRepository")) + repo_settings_edit_request = _RepoSettingsEditRequest.from_dict(obj.get("RepoSettingsEditRequest")) + repo_settings_edit_scope = _RepoSettingsEditScope(obj.get("RepoSettingsEditScope")) + repo_settings_read_raw_request = _RepoSettingsReadRawRequest.from_dict(obj.get("RepoSettingsReadRawRequest")) + repo_settings_read_raw_result = _RepoSettingsReadRawResult.from_dict(obj.get("RepoSettingsReadRawResult")) + repo_settings_write_all_request = _RepoSettingsWriteAllRequest.from_dict(obj.get("RepoSettingsWriteAllRequest")) + repo_settings_write_key_request = _RepoSettingsWriteKeyRequest.from_dict(obj.get("RepoSettingsWriteKeyRequest")) response_format = ResponseFormat.from_dict(obj.get("ResponseFormat")) + sandbox_apply_defaults_request = SandboxApplyDefaultsRequest.from_dict(obj.get("SandboxApplyDefaultsRequest")) + sandbox_check_enforcement_command_result = from_bool(obj.get("SandboxCheckEnforcementCommandResult")) + sandbox_check_platform_support_result = from_bool(obj.get("SandboxCheckPlatformSupportResult")) + sandbox_check_windows_host_loopback_support_result = from_bool(obj.get("SandboxCheckWindowsHostLoopbackSupportResult")) sandbox_config = SandboxConfig.from_dict(obj.get("SandboxConfig")) sandbox_config_auth = SandboxConfigAuth.from_dict(obj.get("SandboxConfigAuth")) sandbox_config_source = SandboxConfigSource(obj.get("SandboxConfigSource")) @@ -52345,20 +57337,44 @@ def from_dict(obj: Any) -> 'RPC': sandbox_config_user_policy_network_proxy = SandboxConfigUserPolicyNetworkProxy.from_dict(obj.get("SandboxConfigUserPolicyNetworkProxy")) sandbox_config_user_policy_seatbelt = SandboxConfigUserPolicySeatbelt.from_dict(obj.get("SandboxConfigUserPolicySeatbelt")) sandbox_credentials_config = SandboxCredentialsConfig.from_dict(obj.get("SandboxCredentialsConfig")) + sandbox_credential_suggestion = SandboxCredentialSuggestion.from_dict(obj.get("SandboxCredentialSuggestion")) + sandbox_credential_suggestions_result = SandboxCredentialSuggestionsResult.from_dict(obj.get("SandboxCredentialSuggestionsResult")) sandbox_disable_for_session_request = SandboxDisableForSessionRequest.from_dict(obj.get("SandboxDisableForSessionRequest")) sandbox_disable_for_session_result = SandboxDisableForSessionResult.from_dict(obj.get("SandboxDisableForSessionResult")) + sandbox_docs_url_result = SandboxDocsURLResult.from_dict(obj.get("SandboxDocsUrlResult")) + sandbox_effective_for_optional_params = _SandboxEffectiveForOptionalParams.from_dict(obj.get("SandboxEffectiveForOptionalParams")) + sandbox_effective_for_optional_result = _SandboxEffectiveForOptionalResult.from_dict(obj.get("SandboxEffectiveForOptionalResult")) + sandbox_enforcement_command_allowed_params = _SandboxEnforcementCommandAllowedParams.from_dict(obj.get("SandboxEnforcementCommandAllowedParams")) sandbox_enforcement_status = SandboxEnforcementStatus.from_dict(obj.get("SandboxEnforcementStatus")) + sandbox_filter_picker_candidates_params = _SandboxFilterPickerCandidatesParams.from_dict(obj.get("SandboxFilterPickerCandidatesParams")) + sandbox_filter_picker_candidates_result = _SandboxFilterPickerCandidatesResult.from_dict(obj.get("SandboxFilterPickerCandidatesResult")) + sandbox_get_unsupported_message_result = from_str(obj.get("SandboxGetUnsupportedMessageResult")) + sandbox_get_unsupported_setting_disabled_notice_result = from_str(obj.get("SandboxGetUnsupportedSettingDisabledNoticeResult")) sandbox_grant_path_for_request_request = SandboxGrantPathForRequestRequest.from_dict(obj.get("SandboxGrantPathForRequestRequest")) sandbox_grant_path_for_request_result = SandboxGrantPathForRequestResult.from_dict(obj.get("SandboxGrantPathForRequestResult")) sandbox_host_capability = SandboxHostCapability.from_dict(obj.get("SandboxHostCapability")) sandbox_host_capability_name = from_str(obj.get("SandboxHostCapabilityName")) sandbox_host_support = SandboxHostSupport.from_dict(obj.get("SandboxHostSupport")) sandbox_masked_env_var = SandboxMaskedEnvVar.from_dict(obj.get("SandboxMaskedEnvVar")) + sandbox_picker_candidate = _SandboxPickerCandidate.from_dict(obj.get("SandboxPickerCandidate")) + sandbox_prepare_picker_policy_params = _SandboxPreparePickerPolicyParams.from_dict(obj.get("SandboxPreparePickerPolicyParams")) + sandbox_prepare_picker_policy_result = _SandboxPreparePickerPolicyResult.from_dict(obj.get("SandboxPreparePickerPolicyResult")) + sandbox_proxy_ca_check_installation_support_result = from_bool(obj.get("SandboxProxyCaCheckInstallationSupportResult")) + sandbox_proxy_ca_check_relevance_result = from_bool(obj.get("SandboxProxyCaCheckRelevanceResult")) sandbox_proxy_ca_create_result = SandboxProxyCACreateResult.from_dict(obj.get("SandboxProxyCaCreateResult")) sandbox_proxy_ca_request = SandboxProxyCARequest.from_dict(obj.get("SandboxProxyCaRequest")) sandbox_proxy_ca_state = SandboxProxyCAState(obj.get("SandboxProxyCaState")) sandbox_proxy_ca_status = SandboxProxyCAStatus.from_dict(obj.get("SandboxProxyCaStatus")) + sandbox_proxy_secret_get_result = SandboxProxySecretGetResult.from_dict(obj.get("SandboxProxySecretGetResult")) + sandbox_proxy_secret_location = SandboxProxySecretLocation.from_dict(obj.get("SandboxProxySecretLocation")) + sandbox_proxy_secret_save_request = SandboxProxySecretSaveRequest.from_dict(obj.get("SandboxProxySecretSaveRequest")) + sandbox_proxy_secret_save_result = SandboxProxySecretSaveResult.from_dict(obj.get("SandboxProxySecretSaveResult")) + sandbox_resolve_floored_config_params = _SandboxResolveFlooredConfigParams.from_dict(obj.get("SandboxResolveFlooredConfigParams")) + sandbox_resolve_floored_config_result = SandboxResolveFlooredConfigResult.from_dict(obj.get("SandboxResolveFlooredConfigResult")) sandbox_session_change = SandboxSessionChange(obj.get("SandboxSessionChange")) + sandbox_settings_apply_defaults_result = from_dict(lambda x: x, obj.get("SandboxSettingsApplyDefaultsResult")) + sandbox_unsupported_startup_notice_params = _SandboxUnsupportedStartupNoticeParams.from_dict(obj.get("SandboxUnsupportedStartupNoticeParams")) + sandbox_unsupported_startup_notice_result = _SandboxUnsupportedStartupNoticeResult.from_dict(obj.get("SandboxUnsupportedStartupNoticeResult")) schedule_add_at_request = ScheduleAddAtRequest.from_dict(obj.get("ScheduleAddAtRequest")) schedule_add_cron_request = ScheduleAddCronRequest.from_dict(obj.get("ScheduleAddCronRequest")) schedule_add_request = ScheduleAddRequest.from_dict(obj.get("ScheduleAddRequest")) @@ -52489,6 +57505,8 @@ def from_dict(obj: Any) -> 'RPC': session_plugins_install_request = SessionPluginsInstallRequest.from_dict(obj.get("SessionPluginsInstallRequest")) session_plugins_marketplaces_refresh_request = SessionPluginsMarketplacesRefreshRequest.from_dict(obj.get("SessionPluginsMarketplacesRefreshRequest")) session_plugins_reload_request = SessionPluginsReloadRequest.from_dict(obj.get("SessionPluginsReloadRequest")) + session_plugins_retry_managed_request = SessionPluginsRetryManagedRequest.from_dict(obj.get("SessionPluginsRetryManagedRequest")) + session_plugins_retry_managed_result = SessionPluginsRetryManagedResult.from_dict(obj.get("SessionPluginsRetryManagedResult")) session_provider_get_endpoint_request = SessionProviderGetEndpointRequest.from_dict(obj.get("SessionProviderGetEndpointRequest")) session_prune_result = SessionPruneResult.from_dict(obj.get("SessionPruneResult")) session_quota_delegate_warning = SessionQuotaDelegateWarning.from_dict(obj.get("SessionQuotaDelegateWarning")) @@ -52585,6 +57603,9 @@ def from_dict(obj: Any) -> 'RPC': session_working_directory_context_with_client = SessionWorkingDirectoryContextWithClient.from_dict(obj.get("SessionWorkingDirectoryContextWithClient")) settable_auth_info = _load_SettableAuthInfo(obj.get("SettableAuthInfo")) settable_token_auth_info = SettableTokenAuthInfo.from_dict(obj.get("SettableTokenAuthInfo")) + settings_edit_operation = _SettingsEditOperation(obj.get("SettingsEditOperation")) + settings_edit_result = _SettingsEditResult.from_dict(obj.get("SettingsEditResult")) + settings_edit_status = _SettingsEditStatus(obj.get("SettingsEditStatus")) shell_cancel_user_requested_request = ShellCancelUserRequestedRequest.from_dict(obj.get("ShellCancelUserRequestedRequest")) shell_credentials = ShellCredentials.from_dict(obj.get("ShellCredentials")) shell_exec_request = ShellExecRequest.from_dict(obj.get("ShellExecRequest")) @@ -52657,6 +57678,8 @@ def from_dict(obj: Any) -> 'RPC': slash_command_show_dialog_result = SlashCommandShowDialogResult.from_dict(obj.get("SlashCommandShowDialogResult")) slash_command_text_result = SlashCommandTextResult.from_dict(obj.get("SlashCommandTextResult")) slash_command_timeline_entry = SlashCommandTimelineEntry.from_dict(obj.get("SlashCommandTimelineEntry")) + staff_log_level_migration_status = StaffLogLevelMigrationStatus(obj.get("StaffLogLevelMigrationStatus")) + staff_update_channel_migration_status = StaffUpdateChannelMigrationStatus(obj.get("StaffUpdateChannelMigrationStatus")) subagent_settings_entry = SubagentSettingsEntry.from_dict(obj.get("SubagentSettingsEntry")) subagent_settings_entry_context_tier = SubagentSettingsEntryContextTier(obj.get("SubagentSettingsEntryContextTier")) system_message_block = SystemMessageBlock.from_dict(obj.get("SystemMessageBlock")) @@ -52771,8 +57794,55 @@ def from_dict(obj: Any) -> 'RPC': user_auth_info = UserAuthInfo.from_dict(obj.get("UserAuthInfo")) user_requested_shell_command_result = UserRequestedShellCommandResult.from_dict(obj.get("UserRequestedShellCommandResult")) user_setting_metadata = UserSettingMetadata.from_dict(obj.get("UserSettingMetadata")) + user_settings_apply_top_key_request = UserSettingsApplyTopKeyRequest.from_dict(obj.get("UserSettingsApplyTopKeyRequest")) + user_settings_apply_top_key_result = UserSettingsApplyTopKeyResult.from_dict(obj.get("UserSettingsApplyTopKeyResult")) + user_settings_build_runtime_request = _UserSettingsBuildRuntimeRequest.from_dict(obj.get("UserSettingsBuildRuntimeRequest")) + user_settings_build_runtime_result = from_dict(lambda x: x, obj.get("UserSettingsBuildRuntimeResult")) + user_settings_edit_request = _UserSettingsEditRequest.from_dict(obj.get("UserSettingsEditRequest")) + user_settings_get_paths_request = UserSettingsGetPathsRequest.from_dict(obj.get("UserSettingsGetPathsRequest")) + user_settings_get_paths_result = UserSettingsGetPathsResult.from_dict(obj.get("UserSettingsGetPathsResult")) user_settings_get_result = UserSettingsGetResult.from_dict(obj.get("UserSettingsGetResult")) + user_settings_governance_keys_result = UserSettingsGovernanceKeysResult.from_dict(obj.get("UserSettingsGovernanceKeysResult")) + user_settings_host_context = UserSettingsHostContext.from_dict(obj.get("UserSettingsHostContext")) + user_settings_load_and_merge_repo_request = _UserSettingsLoadAndMergeRepoRequest.from_dict(obj.get("UserSettingsLoadAndMergeRepoRequest")) + user_settings_load_and_merge_repo_result = _UserSettingsLoadAndMergeRepoResult.from_dict(obj.get("UserSettingsLoadAndMergeRepoResult")) + user_settings_load_for_config_dir_request = _UserSettingsLoadForConfigDirRequest.from_dict(obj.get("UserSettingsLoadForConfigDirRequest")) + user_settings_load_for_config_dir_result = from_dict(lambda x: x, obj.get("UserSettingsLoadForConfigDirResult")) + user_settings_load_for_edit_result = UserSettingsLoadForEditResult.from_dict(obj.get("UserSettingsLoadForEditResult")) + user_settings_load_repo_request = UserSettingsLoadRepoRequest.from_dict(obj.get("UserSettingsLoadRepoRequest")) + user_settings_load_repo_result = UserSettingsLoadRepoResult.from_dict(obj.get("UserSettingsLoadRepoResult")) + user_settings_load_with_warning_result = UserSettingsLoadWithWarningResult.from_dict(obj.get("UserSettingsLoadWithWarningResult")) + user_settings_merge_managed_and_user_params = _UserSettingsMergeManagedAndUserParams.from_dict(obj.get("UserSettingsMergeManagedAndUserParams")) + user_settings_merge_managed_and_user_result = UserSettingsMergeManagedAndUserResult.from_dict(obj.get("UserSettingsMergeManagedAndUserResult")) + user_settings_merge_repo_value_params = _UserSettingsMergeRepoValueParams.from_dict(obj.get("UserSettingsMergeRepoValueParams")) + user_settings_merge_repo_value_result = UserSettingsMergeRepoValueResult.from_dict(obj.get("UserSettingsMergeRepoValueResult")) + user_settings_migrate_obsolete_on_disk_request = _UserSettingsMigrateObsoleteOnDiskRequest.from_dict(obj.get("UserSettingsMigrateObsoleteOnDiskRequest")) + user_settings_migrate_obsolete_on_disk_result = _UserSettingsMigrateObsoleteOnDiskResult.from_dict(obj.get("UserSettingsMigrateObsoleteOnDiskResult")) + user_settings_migrate_obsolete_on_disk_status = _UserSettingsMigrateObsoleteOnDiskStatus(obj.get("UserSettingsMigrateObsoleteOnDiskStatus")) + user_settings_migrate_staff_log_level_once_request = _UserSettingsMigrateStaffLogLevelOnceRequest.from_dict(obj.get("UserSettingsMigrateStaffLogLevelOnceRequest")) + user_settings_migrate_staff_log_level_once_result = _UserSettingsMigrateStaffLogLevelOnceResult.from_dict(obj.get("UserSettingsMigrateStaffLogLevelOnceResult")) + user_settings_migrate_staff_update_channel_once_result = _UserSettingsMigrateStaffUpdateChannelOnceResult.from_dict(obj.get("UserSettingsMigrateStaffUpdateChannelOnceResult")) + user_settings_normalize_request = UserSettingsNormalizeRequest.from_dict(obj.get("UserSettingsNormalizeRequest")) + user_settings_normalize_result = UserSettingsNormalizeResult.from_dict(obj.get("UserSettingsNormalizeResult")) + user_settings_path_result = UserSettingsPathResult.from_dict(obj.get("UserSettingsPathResult")) + user_settings_problem = UserSettingsProblem.from_dict(obj.get("UserSettingsProblem")) + user_settings_problem_location = UserSettingsProblemLocation.from_dict(obj.get("UserSettingsProblemLocation")) + user_settings_problem_source = UserSettingsProblemSource.from_dict(obj.get("UserSettingsProblemSource")) + user_settings_problems_request = UserSettingsProblemsRequest.from_dict(obj.get("UserSettingsProblemsRequest")) + user_settings_problems_result = UserSettingsProblemsResult.from_dict(obj.get("UserSettingsProblemsResult")) + user_settings_reset_staff_model_selection_once_request = _UserSettingsResetStaffModelSelectionOnceRequest.from_dict(obj.get("UserSettingsResetStaffModelSelectionOnceRequest")) + user_settings_reset_staff_model_selection_once_result = _UserSettingsResetStaffModelSelectionOnceResult.from_dict(obj.get("UserSettingsResetStaffModelSelectionOnceResult")) + user_settings_set_hook_disabled_request = _UserSettingsSetHookDisabledRequest.from_dict(obj.get("UserSettingsSetHookDisabledRequest")) + user_settings_set_hook_disabled_result = _UserSettingsSetHookDisabledResult.from_dict(obj.get("UserSettingsSetHookDisabledResult")) user_settings_set_request = UserSettingsSetRequest.from_dict(obj.get("UserSettingsSetRequest")) + user_settings_unknown_keys_warning_request = _UserSettingsUnknownKeysWarningRequest.from_dict(obj.get("UserSettingsUnknownKeysWarningRequest")) + user_settings_unknown_keys_warning_result = _UserSettingsUnknownKeysWarningResult.from_dict(obj.get("UserSettingsUnknownKeysWarningResult")) + user_settings_update_request = _UserSettingsUpdateRequest.from_dict(obj.get("UserSettingsUpdateRequest")) + user_settings_update_result = _UserSettingsUpdateResult.from_dict(obj.get("UserSettingsUpdateResult")) + user_settings_validate_request = UserSettingsValidateRequest.from_dict(obj.get("UserSettingsValidateRequest")) + user_settings_validate_result = UserSettingsValidateResult.from_dict(obj.get("UserSettingsValidateResult")) + user_settings_validation_issue = UserSettingsValidationIssue.from_dict(obj.get("UserSettingsValidationIssue")) + user_settings_write_all_request = UserSettingsWriteAllRequest.from_dict(obj.get("UserSettingsWriteAllRequest")) visibility_get_result = VisibilityGetResult.from_dict(obj.get("VisibilityGetResult")) visibility_set_request = VisibilitySetRequest.from_dict(obj.get("VisibilitySetRequest")) visibility_set_result = VisibilitySetResult.from_dict(obj.get("VisibilitySetResult")) @@ -52864,7 +57934,7 @@ def from_dict(obj: Any) -> 'RPC': subagent_settings = from_union([SubagentSettings.from_dict, from_none], obj.get("SubagentSettings")) task_progress = from_union([TaskProgress.from_dict, from_none], obj.get("TaskProgress")) workspace_summary = from_union([WorkspaceSummary.from_dict, from_none], obj.get("WorkspaceSummary")) - return RPC(abort_request, abort_result, accepted_enqueue_command_result, account_all_users, account_auth_info, account_get_all_users_result, account_get_current_auth_result, account_get_quota_request, account_get_quota_result, account_kind, account_login_request, account_login_result, account_logout_request, account_logout_result, account_quota_snapshot, accounts_enumerate_request, accounts_get_request, accounts_set_request, account_status, adaptive_thinking_support, agent_discovery_path, agent_discovery_path_list, agent_discovery_path_scope, agent_get_current_result, agent_info, agent_info_source, agent_list, agent_list_request, agent_registry_live_target_entry, agent_registry_live_target_entry_attention_kind, agent_registry_live_target_entry_kind, agent_registry_live_target_entry_last_terminal_event, agent_registry_live_target_entry_status, agent_registry_log_capture, agent_registry_log_capture_open_error_reason, agent_registry_spawn_error, agent_registry_spawn_permission_mode, agent_registry_spawn_registry_timeout, agent_registry_spawn_request, agent_registry_spawn_result, agent_registry_spawn_spawned, agent_registry_spawn_validation_error, agent_registry_spawn_validation_error_field, agent_registry_spawn_validation_error_reason, agent_reload_result, agents_custom_agent_initial_model_decision_params, agents_custom_agent_initial_model_decision_result, agents_discover_request, agent_select_request, agent_select_result, agent_set_prompt_request, agents_get_available_builtins_request, agents_get_available_builtins_result, agents_get_builtin_definition_request, agents_get_builtin_definition_result, agents_get_builtin_listing_definition_request, agents_get_builtin_listing_definition_result, agents_get_builtins_result, agents_get_discovery_paths_request, api_key_auth_info, auth_enumerate_query, auth_enumerate_value, auth_identity, auth_identity_metadata, auth_info, auth_info_type, auth_login_account, auth_login_advance_request, auth_login_begin_request, auth_login_begun, auth_login_cancel_request, auth_login_result_dto, auth_login_result_status, auth_login_step, auth_read_query, auth_read_value, auth_status_dto, auth_validation_error, auth_validation_errors, auth_write, auth_write_result, autopilot_objective_credit_limit, autopilot_objective_get_state_result, autopilot_objective_state, autopilot_objective_status, auto_tier_descriptor, auto_tier_metadata, auto_tier_status, builtin_agent_summary, built_in_model_catalog, built_in_model_catalog_entry, builtin_tool_descriptor, builtin_tool_format, builtin_tool_format_type, builtin_tool_input_schema, builtin_tool_input_schema_type, builtin_tool_safe_for_telemetry, builtin_tool_safe_telemetry_fields, cancel_user_requested_shell_command_result, canvas_action, canvas_action_invoke_request, canvas_action_invoke_result, canvas_close_request, canvas_host_context, canvas_host_context_capabilities, canvas_json_schema, canvas_list, canvas_list_open_result, canvas_open_request, canvas_provider_close_request, canvas_provider_invoke_action_request, canvas_provider_open_request, canvas_provider_open_result, canvas_provider_register_request, canvas_provider_unregister_request, canvas_session_context, capi_session_options, card_digest, card_digest_algorithm, card_digest_value, catalog_agent_plugin_candidate, catalog_agent_plugin_candidate_kind, catalog_agent_plugin_candidate_provenance, catalog_agent_plugin_compatibility_tag, catalog_agent_plugin_media_type, catalog_ai_skill_candidate, catalog_ai_skill_candidate_kind, catalog_ai_skill_candidate_provenance, catalog_ai_skill_installability, catalog_ai_skill_media_type, catalog_authentication_required_error, catalog_authentication_required_reason, catalog_candidate, catalog_candidate_kind, catalog_candidate_source, catalog_candidate_source_embedded, catalog_candidate_source_url, catalog_capability, catalog_capability_id, catalog_client_contract, catalog_contract_violation_error, catalog_contract_violation_reason, catalog_handle_rejected_error, catalog_handle_rejection_reason, catalog_handle_type, catalog_invalid_request_error, catalog_invalid_request_field, catalog_malformed_card_error, catalog_malformed_card_reason, catalog_mcp_server_candidate, catalog_mcp_server_candidate_kind, catalog_mcp_server_candidate_provenance, catalog_mcp_server_installability, catalog_media_type, catalog_negotiated_contract, catalog_negotiation_refused_error, catalog_negotiation_refused_reason, catalog_network_failure_error, catalog_network_failure_reason, catalog_not_installable_error, catalog_not_installable_reason, catalog_plugin_repository_source, catalog_policy_rejected_error, catalog_resource_identity, catalog_resource_version, catalog_search_page, catalog_search_pagination, catalog_search_request, catalog_search_result, catalog_search_succeeded, catalog_search_total_count_relation, catalog_selection_cancelled, catalog_selection_decision, catalog_selection_declined, catalog_selection_foreign, catalog_selection_invalid, catalog_selection_replayed, catalog_selection_request, catalog_selection_result, catalog_selection_selected, catalog_selection_stale, catalog_selection_timed_out, catalog_selection_wrong_kind, catalog_trust_eligibility, catalog_trust_provenance, catalog_trust_snapshot, catalog_trust_snapshot_absent, catalog_trust_snapshot_absent_status, catalog_trust_snapshot_current, catalog_trust_snapshot_current_status, catalog_trust_snapshot_downgraded, catalog_trust_snapshot_downgraded_status, catalog_trust_snapshot_malformed, catalog_trust_snapshot_malformed_status, catalog_trust_snapshot_revoked, catalog_trust_snapshot_revoked_status, catalog_trust_snapshot_schema_version, catalog_trust_snapshot_stale, catalog_trust_snapshot_stale_status, catalog_trust_snapshot_unsupported, catalog_trust_snapshot_unsupported_status, catalog_trust_source, catalog_trust_tier, catalog_unavailable_error, catalog_unavailable_reason, catalog_unavailable_transport_error, catalog_unavailable_transport_reason, catalog_unsafe_retrieval_error, catalog_unsafe_retrieval_reason, catalog_unsupported_kind_error, client_metadata, client_task_cancel_reason, client_task_cancel_request, client_task_cancel_result, command_list, commands_finalize_invocation_effect_request, commands_finalize_invocation_effect_result, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_invocation_effect_outcome, commands_invocation_origin, commands_invoke_request, commands_list_request, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, completions_get_trigger_characters_result, completions_request_request, completions_request_result, configure_session_extensions_params, connect_client_info, connected_remote_session_metadata, connected_remote_session_metadata_kind, connected_remote_session_metadata_repository, connector_account_request, connector_authorization_requirement, connector_authorization_scope, connector_availability, connector_capabilities, connector_catalog_entry, connector_catalog_result, connector_catalog_status, connector_connect_request, connector_connect_result, connector_continue_request, connector_disconnect_result, connector_discovery_account, connector_discovery_account_list, connector_discovery_account_request, connector_discovery_auth_info, connector_discovery_availability, connector_discovery_capabilities, connector_discovery_catalog_entry, connector_discovery_catalog_result, connector_mcp_status, connector_reconcile_request, connector_runtime_status, connector_session_account, connector_status, connect_remote_session_params, connect_request, connect_result, content_exclusion_check_paths_request, content_exclusion_check_paths_result, content_exclusion_path_check, content_filter_mode, context_heaviest_message, copilot_api_token_auth_info, copilot_user_response, copilot_user_response_endpoints, copilot_user_response_quota_snapshots, copilot_user_response_quota_snapshots_chat, copilot_user_response_quota_snapshots_completions, copilot_user_response_quota_snapshots_premium_interactions, current_model, current_tool_metadata, customization_reload_outcome, customization_reload_status, customization_reload_subsystem, customizations_reload_result, debug_collect_logs_collected_entry, debug_collect_logs_destination, debug_collect_logs_entry, debug_collect_logs_entry_kind, debug_collect_logs_include, debug_collect_logs_redaction, debug_collect_logs_request, debug_collect_logs_result, debug_collect_logs_result_kind, debug_collect_logs_skipped_entry, debug_collect_logs_source, diagnostic_cursor_status, diagnostic_entry, diagnostic_log_level, diagnostics_configuration, diagnostics_configure_request, diagnostic_severity, diagnostic_source, diagnostic_sources_configuration, diagnostics_read_request, diagnostics_read_result, discovered_canvas, discovered_extension, discovered_extension_mode, discovered_extension_plugin, discovered_extensions, discovered_extensions_disable_request, discovered_extensions_enable_request, discovered_extension_source, discovered_hook, discovered_mcp_server, discovered_mcp_server_type, discovered_model, discovered_model_list, enqueue_command_params, enqueue_command_result, env_auth_info, environment_capabilities, environment_kind, environments_delete_request, environments_delete_result, environments_get_request, environments_get_result, environments_list_request, environments_list_result, event_log_read_request, event_log_release_interest_result, event_log_tail_result, event_log_types, events_agent_scope, events_cursor_status, events_read_direction, events_read_result, execute_command_params, execute_command_result, extension, extension_context_push_input, extension_launch_profile, extension_launch_provider_resolve_request, extension_launch_provider_resolve_result, extension_list, extensions_disable_request, extensions_enable_request, extension_source, extension_status, external_tool_result, external_tool_text_result_for_llm, external_tool_text_result_for_llm_binary_results_for_llm, external_tool_text_result_for_llm_binary_results_for_llm_type, external_tool_text_result_for_llm_content, external_tool_text_result_for_llm_content_audio, external_tool_text_result_for_llm_content_image, external_tool_text_result_for_llm_content_resource, external_tool_text_result_for_llm_content_resource_details, external_tool_text_result_for_llm_content_resource_link, external_tool_text_result_for_llm_content_resource_link_icon, external_tool_text_result_for_llm_content_resource_link_icon_theme, external_tool_text_result_for_llm_content_shell_exit, external_tool_text_result_for_llm_content_terminal, external_tool_text_result_for_llm_content_text, filter_mapping, fleet_start_request, fleet_start_result, folder_trust_add_params, folder_trust_check_params, folder_trust_check_result, gh_cli_auth_info, git_current_branch_remote_result, git_cwd_request, git_hub_environment, git_hub_owner_option, git_hub_owners_cancel_request, git_hub_owners_cancel_result, git_hub_owners_list_request, git_hub_owners_list_result, git_hub_owners_request_id_result, git_hub_repository_at_path_request, git_hub_repository_at_path_result, git_hub_repository_identity, git_hub_telemetry_client_info, git_hub_telemetry_event, git_hub_telemetry_notification, git_hub_token_acquire_reason, git_hub_token_acquire_request, git_hub_token_acquire_result, git_remote_repository, git_repos_from_remotes_request, git_repos_from_remotes_result, global_state_load_for_config_dir_request, global_state_load_result, global_state_write_key_request, handle_pending_tool_call_request, handle_pending_tool_call_result, history_abort_manual_compaction_result, history_cancel_background_compaction_result, history_clear_context_request, history_clear_context_result, history_compact_context_window, history_compact_request, history_compact_result, history_file_restore_skip_reason, history_list_rewind_points_result, history_preview_rewind_request, history_preview_rewind_result, history_rewind_change_type, history_rewind_file_preview, history_rewind_mode, history_rewind_outcome, history_rewind_point, history_rewind_request, history_rewind_result, history_rewind_unavailable_reason, history_skipped_file_restore, history_summarize_for_handoff_result, history_truncate_request, history_truncate_result, hmac_auth_info, hook_invoke_request, hook_invoke_response, hook_origin, hooks_discover_request, hooks_discover_result, hook_type, host_configuration, host_dispose_request, host_empty_result, host_environment_credentials, host_exited_notification, host_exit_reason, host_git_hub_environment_options, host_list_sessions_request, host_list_sessions_result, host_local_server_configuration, host_local_server_options, host_publish_session_request, host_publish_session_result, host_ready_request, host_register_session_request, host_session_create_callback, host_session_create_request, host_session_create_result, host_session_released_notification, host_session_release_request, host_session_summary, host_start_request, host_start_result, installation_catalogue_identity, installation_confirmation_request, installation_confirmation_response, installation_decision, installation_review, installed_plugin, installed_plugin_info, installed_plugin_source, installed_plugin_source_git_hub, installed_plugin_source_local, installed_plugin_source_url, instruction_discovery_path, instruction_discovery_path_kind, instruction_discovery_path_list, instruction_discovery_path_location, instructions_discover_request, instructions_get_discovery_paths_request, instructions_get_sources_result, instruction_source, instruction_source_location, instruction_source_type, interrupt_main_turn_request, interrupt_main_turn_result, json_schema_response_format, llm_inference_headers, llm_inference_http_request_chunk_request, llm_inference_http_request_chunk_result, llm_inference_http_request_start_request, llm_inference_http_request_start_result, llm_inference_http_request_start_transport, llm_inference_http_response_chunk_error, llm_inference_http_response_chunk_request, llm_inference_http_response_chunk_result, llm_inference_http_response_start_request, llm_inference_http_response_start_result, llm_inference_set_provider_result, local_session_metadata_value, logged_in_user, login_provider_kind, log_request, log_result, lsp_initialize_request, managed_mcp_server_config, managed_setting_meta, managed_settings_channel, managed_settings_compose_layer, managed_settings_compose_request, managed_settings_compose_result, managed_settings_diagnostic, managed_settings_diagnostic_severity, managed_settings_layer, managed_settings_meta, managed_settings_read_result, managed_settings_resolved_data, managed_settings_resolve_request, managed_settings_resolve_result, managed_settings_schema_result, managed_settings_validate_request, managed_settings_validate_result, managed_settings_values, marketplace_add_result, marketplace_browse_result, marketplace_info, marketplace_list_result, marketplace_plugin_info, marketplace_refresh_entry, marketplace_refresh_result, marketplace_remove_result, mcp_allowed_server, mcp_apply_install_request, mcp_apply_uninstall_request, mcp_apps_call_tool_request, mcp_apps_diagnose_capability, mcp_apps_diagnose_request, mcp_apps_diagnose_result, mcp_apps_diagnose_server, mcp_apps_host_context, mcp_apps_host_context_details, mcp_apps_host_context_details_available_display_mode, mcp_apps_host_context_details_display_mode, mcp_apps_host_context_details_platform, mcp_apps_host_context_details_theme, mcp_apps_list_tools_request, mcp_apps_list_tools_result, mcp_apps_read_resource_request, mcp_apps_read_resource_result, mcp_apps_resource_content, mcp_apps_set_host_context_details, mcp_apps_set_host_context_details_available_display_mode, mcp_apps_set_host_context_details_display_mode, mcp_apps_set_host_context_details_platform, mcp_apps_set_host_context_details_theme, mcp_apps_set_host_context_request, mcp_cancel_sampling_execution_params, mcp_cancel_sampling_execution_result, mcp_config_add_request, mcp_config_disable_request, mcp_config_enable_request, mcp_config_list, mcp_config_remove_request, mcp_config_update_request, mcp_configured_server, mcp_configured_server_list, mcp_configured_server_state, mcp_configure_git_hub_request, mcp_configure_git_hub_result, mcp_diagnostic_details, mcp_diagnostic_direction, mcp_diagnostic_kind, mcp_diagnostic_source_configuration, mcp_disable_request, mcp_discover_request, mcp_discover_result, mcp_elicitation_form_mode, mcp_enable_request, mcp_execute_sampling_params, mcp_execute_sampling_request, mcp_execute_sampling_result, mcp_failed_server, mcp_filtered_server, mcp_headers_handle_pending_headers_refresh_request, mcp_headers_handle_pending_headers_refresh_request_request, mcp_headers_handle_pending_headers_refresh_request_result, mcp_host_state, mcp_installation_failure_reason, mcp_installation_input, mcp_installation_management_outcome, mcp_installation_management_result, mcp_installation_operation_request, mcp_installation_operation_status, mcp_installation_outcome, mcp_installation_remote_configuration, mcp_installation_result, mcp_installation_review, mcp_installation_secret, mcp_installation_secret_storage, mcp_installations_request, mcp_installation_state, mcp_installation_summary, mcp_install_plan, mcp_is_server_running_request, mcp_is_server_running_result, mcp_list_tools_request, mcp_list_tools_result, mcp_oauth_authentication_state_changed_request, mcp_oauth_cancel_login_request, mcp_oauth_cancel_login_result, mcp_oauth_complete_request, mcp_oauth_handle_pending_request, mcp_oauth_handle_pending_result, mcp_oauth_login_grant_type, mcp_oauth_login_request, mcp_oauth_login_result, mcp_oauth_pending_request_response, mcp_oauth_prepare_login_request, mcp_oauth_prepare_login_result, mcp_oauth_probe_needs_auth_reason, mcp_oauth_probe_request, mcp_oauth_probe_result, mcp_oauth_respond_request, mcp_oauth_respond_result, mcp_owned_oauth_login_status, mcp_plan_configuration_change, mcp_plan_configuration_operation, mcp_plan_enum_value_type, mcp_plan_install_planned, mcp_plan_install_request, mcp_plan_install_result, mcp_plan_install_source, mcp_plan_install_source_candidate, mcp_plan_install_source_candidate_kind, mcp_plan_install_source_card, mcp_plan_install_source_card_kind, mcp_plan_package_install_method, mcp_plan_package_transport, mcp_plan_policy_decision, mcp_plan_policy_result, mcp_plan_policy_source, mcp_plan_provenance, mcp_plan_remote_install_method, mcp_plan_remote_transport, mcp_plan_required_value, mcp_plan_required_value_enum, mcp_plan_required_value_enum_kind, mcp_plan_required_value_scalar, mcp_plan_required_value_scalar_kind, mcp_plan_resource_identity, mcp_plan_scalar_value_type, mcp_plan_scope, mcp_plan_secret_placeholder, mcp_plan_secret_reference, mcp_plan_target, mcp_plan_transport_choice, mcp_plan_transport_choice_package, mcp_plan_transport_choice_remote, mcp_plan_uninstall_request, mcp_plan_value_category, mcp_prepared_install, mcp_prepare_install_request, mcp_prompt, mcp_prompt_argument, mcp_prompt_icon, mcp_prompt_message, mcp_prompt_role, mcp_prompts_get_request, mcp_prompts_get_result, mcp_prompts_list_request, mcp_prompts_list_result, mcp_register_external_client_request, mcp_reload_config, mcp_reload_with_config_request, mcp_remove_git_hub_result, mcp_resource, mcp_resource_annotations, mcp_resource_content, mcp_resource_icon, mcp_resources_list_request, mcp_resources_list_result, mcp_resources_list_templates_request, mcp_resources_list_templates_result, mcp_resources_read_request, mcp_resources_read_result, mcp_resource_template, mcp_restart_server_request, mcp_safe_for_telemetry, mcp_safe_for_telemetry_fields, mcp_sampling_execution_action, mcp_sampling_execution_result, mcp_serializable_server_config, mcp_server, mcp_server_auth_config, mcp_server_auth_config_redirect_port, mcp_server_card_embedded, mcp_server_card_embedded_kind, mcp_server_card_media_type, mcp_server_card_reference, mcp_server_card_url, mcp_server_card_url_kind, mcp_server_config, mcp_server_config_defer_tools, mcp_server_config_http, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_memory, mcp_server_config_memory_type, mcp_server_config_stdio, mcp_server_config_stdio_type, mcp_server_failure_info, mcp_server_list, mcp_server_needs_auth_info, mcp_server_ownership, mcp_set_env_value_mode_details, mcp_set_env_value_mode_params, mcp_set_env_value_mode_result, mcp_source_file, mcp_source_plugin, mcp_source_ref, mcp_start_server_request, mcp_start_servers_result, mcp_stop_server_request, mcp_task_metadata, mcp_tools, mcp_tool_ui, mcp_tool_ui_visibility, mcp_uninstall_plan, mcp_unregister_external_client_request, memory_configuration, metadata_context_attribution_result, metadata_context_heaviest_messages_request, metadata_context_heaviest_messages_result, metadata_context_info_request, metadata_context_info_result, metadata_is_processing_result, metadata_recompute_context_tokens_request, metadata_recompute_context_tokens_result, metadata_record_context_change_request, metadata_record_context_change_result, metadata_set_working_directory_request, metadata_set_working_directory_result, metadata_snapshot_current_mode, metadata_snapshot_remote_metadata, metadata_snapshot_remote_metadata_repository, metadata_snapshot_remote_metadata_task_type, metadata_update_client_metadata_request, model, model_apply_startup_overlay_request, model_artifact_details, model_billing, model_billing_promo, model_billing_token_prices, model_billing_token_prices_long_context, model_capabilities, model_capabilities_limits, model_capabilities_limits_vision, model_capabilities_override, model_capabilities_override_limits, model_capabilities_override_limits_vision, model_capabilities_override_supports, model_capabilities_supports, model_clear_startup_seed_request, model_clear_startup_seed_result, model_list, model_list_request, model_message, model_picker_category, model_picker_persistence_request, model_picker_price_category, model_picker_settings_context, model_policy, model_policy_state, model_provider_adapter_catalog, model_provider_adapter_descriptor, model_provider_adapter_operation_descriptor, model_provider_attribution, model_provider_automatic_discovery_mode, model_provider_automatic_discovery_policy, model_provider_configuration_disposition, model_provider_configuration_plan, model_provider_descriptor, model_provider_discover_request, model_provider_discover_result, model_provider_discovery_network_scope, model_provider_get_status_request, model_provider_instance, model_provider_instance_reference, model_provider_models_list_request, model_provider_operation_outcome, model_provider_operation_outcome_code, model_provider_prepare_configuration_request, model_provider_provenance, model_provider_provenance_source, model_provider_status, model_provider_warning, model_set_allowed_models_request, model_set_allowed_models_result, model_set_reasoning_effort_request, model_set_reasoning_effort_result, models_list_request, model_switch_auto_tier_request, model_switch_auto_tier_result, model_switch_auto_tier_status, model_switch_confirmation, model_switch_to_request, model_switch_to_result, model_warning_text, mode_set_request, mode_set_result, move_mcp_loading_to_background_result, named_provider_config, name_get_result, name_set_auto_request, name_set_auto_result, name_set_request, open_canvas_instance, options_update_additional_content_exclusion_policy, options_update_additional_content_exclusion_policy_rule, options_update_additional_content_exclusion_policy_rule_source, options_update_additional_content_exclusion_policy_scope, options_update_context_tier, options_update_env_value_mode, options_update_reasoning_summary, options_update_tool_filter_precedence, pending_external_tool_request, pending_external_tool_request_list, pending_permission_request, pending_permission_request_list, permission_decision, permission_decision_approved, permission_decision_approved_for_location, permission_decision_approved_for_session, permission_decision_approve_for_location, permission_decision_approve_for_location_approval, permission_decision_approve_for_location_approval_commands, permission_decision_approve_for_location_approval_custom_tool, permission_decision_approve_for_location_approval_extension_env_access, permission_decision_approve_for_location_approval_extension_management, permission_decision_approve_for_location_approval_extension_permission_access, permission_decision_approve_for_location_approval_mcp, permission_decision_approve_for_location_approval_mcp_sampling, permission_decision_approve_for_location_approval_memory, permission_decision_approve_for_location_approval_read, permission_decision_approve_for_location_approval_workflow, permission_decision_approve_for_location_approval_write, permission_decision_approve_for_session, permission_decision_approve_for_session_approval, permission_decision_approve_for_session_approval_commands, permission_decision_approve_for_session_approval_custom_tool, permission_decision_approve_for_session_approval_extension_env_access, permission_decision_approve_for_session_approval_extension_management, permission_decision_approve_for_session_approval_extension_permission_access, permission_decision_approve_for_session_approval_mcp, permission_decision_approve_for_session_approval_mcp_sampling, permission_decision_approve_for_session_approval_memory, permission_decision_approve_for_session_approval_read, permission_decision_approve_for_session_approval_workflow, permission_decision_approve_for_session_approval_write, permission_decision_approve_once, permission_decision_approve_permanently, permission_decision_approve_read_only_for_session, permission_decision_cancelled, permission_decision_context, permission_decision_denied_by_content_exclusion_policy, permission_decision_denied_by_permission_request_hook, permission_decision_denied_by_rules, permission_decision_denied_interactively_by_user, permission_decision_denied_no_approval_rule_and_could_not_request_from_user, permission_decision_outcome, permission_decision_reject, permission_decision_request, permission_decision_surface, permission_decision_user_not_available, permission_location_add_tool_approval_params, permission_location_apply_params, permission_location_apply_result, permission_location_resolve_params, permission_location_resolve_result, permission_location_type, permission_mode_source, permission_paths_add_params, permission_paths_allowed_check_params, permission_paths_allowed_check_result, permission_paths_config, permission_paths_list, permission_paths_update_primary_params, permission_paths_workspace_check_params, permission_paths_workspace_check_result, permission_prompt_shown_notification, permission_request_result, permission_response_capability, permission_rules_set, permissions_configure_additional_content_exclusion_policy, permissions_configure_additional_content_exclusion_policy_rule, permissions_configure_additional_content_exclusion_policy_rule_source, permissions_configure_additional_content_exclusion_policy_scope, permissions_configure_params, permissions_configure_result, permissions_folder_trust_add_trusted_result, permissions_get_mode_request, permissions_get_mode_result, permissions_locations_add_tool_approval_details, permissions_locations_add_tool_approval_details_commands, permissions_locations_add_tool_approval_details_custom_tool, permissions_locations_add_tool_approval_details_extension_env_access, permissions_locations_add_tool_approval_details_extension_management, permissions_locations_add_tool_approval_details_extension_permission_access, permissions_locations_add_tool_approval_details_mcp, permissions_locations_add_tool_approval_details_mcp_sampling, permissions_locations_add_tool_approval_details_memory, permissions_locations_add_tool_approval_details_read, permissions_locations_add_tool_approval_details_workflow, permissions_locations_add_tool_approval_details_write, permissions_locations_add_tool_approval_result, permissions_modify_rules_params, permissions_modify_rules_result, permissions_modify_rules_scope, permissions_notify_prompt_shown_result, permissions_paths_add_result, permissions_paths_list_request, permissions_paths_update_primary_result, permissions_pending_requests_request, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_approve_all_request, permissions_set_approve_all_result, permissions_set_approve_all_source, permissions_set_mode_request, permissions_set_mode_result, permissions_set_required_request, permissions_set_required_result, permissions_urls_set_unrestricted_mode_result, permission_urls_config, permission_urls_set_unrestricted_mode_params, ping_request, ping_result, plan_read_result, plan_read_sql_todos_result, plan_read_sql_todos_with_dependencies_result, plan_sql_todo_dependency, plan_sql_todos_row, plan_update_request, plugin, plugin_install_result, plugin_install_staging_mode, plugin_list, plugin_list_result, plugins_builtin_set_request, plugins_disable_request, plugins_enable_request, plugins_install_request, plugins_marketplaces_add_request, plugins_marketplaces_browse_request, plugins_marketplaces_refresh_request, plugins_marketplaces_remove_request, plugins_reload_request, plugins_uninstall_request, plugins_update_request, plugin_update_all_entry, plugin_update_all_result, plugin_update_result, protocol_append_mode, protocol_customize_mode, protocol_external_tool_defer, protocol_external_tool_definition, protocol_marker_section_override, protocol_replace_mode, protocol_section_override, protocol_static_section_action, protocol_static_section_override, protocol_system_message_append_config, protocol_system_message_config, protocol_system_message_customize_config, protocol_system_message_replace_config, provider_add_request, provider_add_result, provider_config, provider_config_azure, provider_config_model_provider, provider_config_transport, provider_config_type, provider_config_wire_api, provider_descriptor, provider_endpoint, provider_endpoint_transport, provider_endpoint_type, provider_endpoint_wire_api, provider_get_endpoint_request, provider_model_config, provider_session_token, provider_sync_request, provider_sync_result, provider_token_acquire_request, provider_token_acquire_result, provider_withdraw_request, provider_withdraw_result, push_attachment, push_attachment_blob, push_attachment_directory, push_attachment_file, push_attachment_file_line_range, push_attachment_git_hub_actions_job, push_attachment_git_hub_commit, push_attachment_git_hub_file, push_attachment_git_hub_file_diff, push_attachment_git_hub_file_diff_side, push_attachment_git_hub_reference, push_attachment_git_hub_reference_type, push_attachment_git_hub_release, push_attachment_git_hub_repository, push_attachment_git_hub_snippet, push_attachment_git_hub_tree_comparison, push_attachment_git_hub_tree_comparison_side, push_attachment_git_hub_url, push_attachment_selection, push_attachment_selection_details, push_attachment_selection_details_end, push_attachment_selection_details_start, push_git_hub_repo_ref, queue_append_steering_request, queue_begin_deferred_idle_drain_request, queue_begin_deferred_idle_drain_result, queue_consume_system_notifications_request, queued_command_handled, queued_command_not_handled, queued_command_result, queue_defer_session_idle_request, queue_duplicate_at_request, queue_duplicate_at_result, queue_enqueue_resume_pending_result, queue_finish_deferred_idle_drain_request, queue_finish_deferred_idle_drain_result, queue_has_pending_result, queue_insert_at_request, queue_insert_at_result, queue_insert_message, queue_move_item_request, queue_move_item_result, queue_pending_items, queue_pending_items_kind, queue_pending_items_result, queue_remove_at_request, queue_remove_at_result, queue_remove_most_recent_result, queue_send_now_request, queue_send_now_result, queue_set_drain_paused_request, queue_snapshot_result, queue_update_text_request, queue_update_text_result, queue_withdraw_message_request, queue_withdraw_message_result, quota_take_warnings_result, quota_warning_projection, register_event_interest_params, register_event_interest_result, release_event_interest_params, remote_control_config, remote_control_config_existing_mc_session, remote_control_status, remote_control_status_active, remote_control_status_connecting, remote_control_status_error, remote_control_status_off, remote_control_status_result, remote_control_stop_result, remote_control_transfer_result, remote_enable_request, remote_enable_result, remote_notify_steerable_changed_request, remote_notify_steerable_changed_result, remote_session_connection_result, remote_session_host_status, remote_session_metadata_repository, remote_session_metadata_task_type, remote_session_metadata_value, remote_session_mode, remote_session_repository, response_format, sandbox_config, sandbox_config_auth, sandbox_config_source, sandbox_config_user_policy, sandbox_config_user_policy_experimental, sandbox_config_user_policy_experimental_seatbelt, sandbox_config_user_policy_filesystem, sandbox_config_user_policy_network, sandbox_config_user_policy_network_proxy, sandbox_config_user_policy_seatbelt, sandbox_credentials_config, sandbox_disable_for_session_request, sandbox_disable_for_session_result, sandbox_enforcement_status, sandbox_grant_path_for_request_request, sandbox_grant_path_for_request_result, sandbox_host_capability, sandbox_host_capability_name, sandbox_host_support, sandbox_masked_env_var, sandbox_proxy_ca_create_result, sandbox_proxy_ca_request, sandbox_proxy_ca_state, sandbox_proxy_ca_status, sandbox_session_change, schedule_add_at_request, schedule_add_cron_request, schedule_add_request, schedule_add_result, schedule_add_self_paced_request, schedule_entry, schedule_has_self_paced_result, schedule_list, schedule_rearm_self_paced_request, schedule_stop_request, schedule_stop_result, secrets_add_filter_values_request, secrets_add_filter_values_result, send_agent_mode, send_attachments_to_message_params, send_message_item, send_messages_request, send_messages_result, send_mode, send_request, send_result, send_system_notification_request, server_agent_list, server_instruction_source_list, server_skill, server_skill_list, session_activity, session_agent_list_request, session_auth_login_request, session_auth_logout_user_request, session_auth_status, session_auth_switch_request, session_bulk_delete_result, session_cancel_all_background_agents_result, session_capability, session_commands_list_request, session_completion_item, session_connected_ide_info, session_context, session_context_host_type, session_enrich_metadata_result, session_fs_append_file_request, session_fs_error, session_fs_error_code, session_fs_exists_request, session_fs_exists_result, session_fs_mkdir_request, session_fs_readdir_request, session_fs_readdir_result, session_fs_readdir_with_types_entry, session_fs_readdir_with_types_entry_type, session_fs_readdir_with_types_request, session_fs_readdir_with_types_result, session_fs_read_file_bytes_request, session_fs_read_file_bytes_result, session_fs_read_file_request, session_fs_read_file_result, session_fs_rename_request, session_fs_rm_request, session_fs_set_provider_capabilities, session_fs_set_provider_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_sqlite_exists_request, session_fs_sqlite_exists_result, session_fs_sqlite_query_request, session_fs_sqlite_query_result, session_fs_sqlite_query_type, session_fs_sqlite_transaction_error, session_fs_sqlite_transaction_error_class, session_fs_sqlite_transaction_request, session_fs_sqlite_transaction_result, session_fs_sqlite_transaction_statement, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_bytes_request, session_fs_write_file_request, session_git_hub_auth_get_all_auth_available_result, session_git_hub_auth_logout_result, session_git_hub_auth_logout_user_result, session_history_compact_request, session_installed_plugin, session_installed_plugin_source, session_installed_plugin_source_git_hub, session_installed_plugin_source_local, session_installed_plugin_source_url, session_limit_prediction_baseline_data, session_limit_prediction_client_type, session_limit_prediction_details, session_limit_prediction_predict_request, session_limit_prediction_request, session_limit_prediction_result, session_limit_prediction_source, session_limit_prediction_tier, session_limit_prediction_tier_option, session_limit_prediction_unavailable_reason, session_list, session_list_entry, session_list_filter, session_load_deferred_repo_hooks_result, session_log_level, session_managed_permissions, session_managed_settings, session_mcp_apps_call_tool_result, session_mcp_oauth_cancel_login_request, session_mcp_oauth_cancel_login_result, session_mcp_oauth_prepare_login_request, session_mcp_oauth_prepare_login_result, session_mcp_set_connected_ide_info_params, session_metadata_snapshot, session_mode, session_model_clear_startup_seed_request, session_model_clear_startup_seed_result, session_model_list, session_model_list_request, session_model_price_category, session_open_options, session_open_options_additional_content_exclusion_policy, session_open_options_additional_content_exclusion_policy_rule, session_open_options_additional_content_exclusion_policy_rule_source, session_open_options_additional_content_exclusion_policy_scope, session_open_options_env_value_mode, session_open_options_reasoning_summary, session_open_params, session_open_result, session_plugins_disable_request, session_plugins_enable_request, session_plugins_install_request, session_plugins_marketplaces_refresh_request, session_plugins_reload_request, session_provider_get_endpoint_request, session_prune_result, session_quota_delegate_warning, session_quota_get_result, session_quota_plan_tier, session_quota_projection, session_quota_refresh_result, session_quota_snapshot, session_quota_take_warnings_result, sessions_bulk_delete_request, sessions_check_in_use_request, sessions_check_in_use_result, sessions_client_metadata_entry, sessions_close_request, sessions_close_result, sessions_create_workspace_request, sessions_create_workspace_result, sessions_delete_request, sessions_enrich_metadata_request, session_set_credentials_params, session_set_credentials_result, session_settings_built_in_tool_availability_snapshot, session_settings_evaluate_predicate_request, session_settings_evaluate_predicate_result, session_settings_job_snapshot, session_settings_model_snapshot, session_settings_online_evaluation_snapshot, session_settings_predicate_name, session_settings_repo_snapshot, session_settings_snapshot, session_settings_validation_snapshot, sessions_find_by_prefix_request, sessions_find_by_prefix_result, sessions_find_by_task_id_request, sessions_find_by_task_id_result, sessions_fork_request, sessions_fork_result, sessions_get_board_entry_count_request, sessions_get_board_entry_count_result, sessions_get_client_metadata_request, sessions_get_client_metadata_result, sessions_get_event_file_path_request, sessions_get_event_file_path_result, sessions_get_last_for_context_request, sessions_get_last_for_context_result, sessions_get_metadata_request, sessions_get_metadata_result, sessions_get_persisted_remote_steerable_request, sessions_get_persisted_remote_steerable_result, session_sizes, sessions_list_non_empty_session_ids_request, sessions_list_non_empty_session_ids_result, sessions_list_request, sessions_load_deferred_repo_hooks_request, sessions_load_workspace_request, sessions_load_workspace_result, sessions_open_attach, sessions_open_cloud, sessions_open_create, sessions_open_handoff, sessions_open_handoff_task_type, sessions_open_progress, sessions_open_progress_status, sessions_open_progress_step, sessions_open_remote, sessions_open_resume, sessions_open_resume_last, sessions_open_status, session_source, sessions_prune_old_request, sessions_read_persisted_events_request, sessions_release_lock_request, sessions_release_lock_result, sessions_reload_plugin_hooks_request, sessions_reload_plugin_hooks_result, sessions_save_request, sessions_save_result, sessions_set_additional_plugins_request, sessions_set_additional_plugins_result, sessions_set_remote_control_steering_request, sessions_start_remote_control_request, sessions_stop_remote_control_request, sessions_transfer_remote_control_request, sessions_update_workspace_fields_request, sessions_update_workspace_fields_result, session_telemetry_engagement, session_update_options_params, session_update_options_result, session_usage_set_code_changes_request, session_visibility_status, session_workflow_pause_at_checkpoint_result, session_working_directory_context, session_working_directory_context_host_type, session_working_directory_context_with_client, settable_auth_info, settable_token_auth_info, shell_cancel_user_requested_request, shell_credentials, shell_exec_request, shell_exec_result, shell_execute_user_requested_request, shell_init_profile, shell_init_script, shell_init_script_shell, shell_kill_request, shell_kill_result, shell_kill_signal, shell_options, shutdown_request, skill, skill_apply_install_request, skill_apply_uninstall_request, skill_discovery_path, skill_discovery_path_list, skill_discovery_scope, skill_installation_failure_reason, skill_installation_file_review, skill_installation_location, skill_installation_management_outcome, skill_installation_management_result, skill_installation_operation_request, skill_installation_operation_status, skill_installation_outcome, skill_installation_ownership_state, skill_installation_result, skill_installation_review, skill_installation_scope, skill_installation_session_state, skill_installation_source, skill_installations_request, skill_installation_summary, skill_install_plan, skill_list, skill_plan_install_request, skill_plan_uninstall_request, skill_provider_descriptor, skill_provider_list_request, skill_provider_list_result, skill_provider_read_request, skill_provider_read_result, skills_config_set_disabled_skills_request, skills_config_set_skill_disabled_request, skills_disable_request, skills_discover_request, skills_enable_request, skill_set_enabled_request, skills_get_discovery_paths_request, skills_get_invoked_result, skills_invoked_skill, skills_load_diagnostics, skill_uninstall_plan, slash_command_add_timeline_entry_result, slash_command_agent_prompt_result, slash_command_completed_result, slash_command_info, slash_command_input, slash_command_input_choice, slash_command_input_completion, slash_command_invocation_result, slash_command_kind, slash_command_model_picker_dialog, slash_command_select_subcommand_option, slash_command_select_subcommand_result, slash_command_set_model_result, slash_command_set_plan_model_result, slash_command_show_dialog_result, slash_command_text_result, slash_command_timeline_entry, subagent_settings_entry, subagent_settings_entry_context_tier, system_message_block, task_agent_info, task_agent_progress, task_client_active_status, task_client_execution_mode, task_client_info, task_client_owner, task_client_owner_kind, task_client_owner_presence, task_client_progress, task_client_status, task_client_type, task_client_update, task_complete_data, task_completion_decision, task_execution_mode, task_info, task_kind, task_list, task_progress_line, tasks_cancel_request, tasks_cancel_result, tasks_get_current_promotable_result, tasks_get_progress_request, tasks_get_progress_result, task_shell_info, task_shell_info_attachment_mode, task_shell_progress, tasks_promote_current_to_background_result, tasks_promote_to_background_request, tasks_promote_to_background_result, tasks_refresh_result, tasks_register_request, tasks_register_result, tasks_remove_request, tasks_remove_result, tasks_send_message_request, tasks_send_message_result, tasks_start_agent_request, tasks_start_agent_result, task_status, tasks_update_request, tasks_update_result, tasks_wait_for_pending_result, telemetry_set_feature_overrides_request, token_auth_info, token_provider_auth_info, tool, tool_list, tool_result, tool_result_expanded, tool_result_new_message, tool_result_type, tools_execute_request, tools_get_builtin_descriptors_request, tools_get_builtin_descriptors_result, tools_get_current_metadata_result, tools_initialize_and_validate_result, tools_list_pending_requests_request, tools_list_request, tools_set_request, tools_set_result, tools_shell_descriptor_config, tools_task_complete_event_data_request, tools_update_subagent_settings_result, ui_auto_mode_switch_response, ui_elicitation_array_any_of_field, ui_elicitation_array_any_of_field_items, ui_elicitation_array_any_of_field_items_any_of, ui_elicitation_array_enum_field, ui_elicitation_array_enum_field_items, ui_elicitation_field_value, ui_elicitation_request, ui_elicitation_response, ui_elicitation_response_action, ui_elicitation_response_content, ui_elicitation_result, ui_elicitation_schema, ui_elicitation_schema_property, ui_elicitation_schema_property_boolean, ui_elicitation_schema_property_number, ui_elicitation_schema_property_number_type, ui_elicitation_schema_property_string, ui_elicitation_schema_property_string_format, ui_elicitation_string_enum_field, ui_elicitation_string_one_of_field, ui_elicitation_string_one_of_field_one_of, ui_ephemeral_query_request, ui_ephemeral_query_result, ui_exit_plan_mode_action, ui_exit_plan_mode_response, ui_handle_pending_auto_mode_switch_request, ui_handle_pending_elicitation_request, ui_handle_pending_exit_plan_mode_request, ui_handle_pending_result, ui_handle_pending_sampling_request, ui_handle_pending_sampling_response, ui_handle_pending_session_limits_exhausted_request, ui_handle_pending_user_input_request, ui_register_direct_auto_mode_switch_handler_result, ui_session_limits_exhausted_response, ui_session_limits_exhausted_response_action, ui_unregister_direct_auto_mode_switch_handler_request, ui_unregister_direct_auto_mode_switch_handler_result, ui_user_input_response, unsupported_enqueue_command_result, update_subagent_settings_request, usage_get_metrics_result, usage_set_code_changes_request, user_auth_info, user_requested_shell_command_result, user_setting_metadata, user_settings_get_result, user_settings_set_request, visibility_get_result, visibility_set_request, visibility_set_result, workflow_abort_request, workflow_ack_result, workflow_agent_options, workflow_agent_request, workflow_agent_result, workflow_agent_summary, workflow_cancel_request, workflow_current_phase, workflow_declared_limits, workflow_durable_operation, workflow_execute_request, workflow_execute_result, workflow_get_run_progress_request, workflow_get_run_request, workflow_journal_get_request, workflow_journal_get_result, workflow_journal_put_request, workflow_list_runs_request, workflow_list_runs_result, workflow_log_line, workflow_log_line_kind, workflow_log_request, workflow_pause_checkpoint_action, workflow_pause_checkpoint_request, workflow_pause_checkpoint_result, workflow_pause_info, workflow_pause_request, workflow_phase_observation, workflow_phase_status, workflow_progress_line, workflow_progress_page, workflow_resume_request, workflow_resume_result, workflow_run_consumed, workflow_run_detail, workflow_run_failure, workflow_run_failure_kind, workflow_run_limits, workflow_run_options, workflow_run_request, workflow_run_result, workflow_run_status, workflow_run_summary, workflow_run_terminal, workflow_tool_resume_request, workflow_tool_run_options, workflow_tool_run_request, workspace_diff_contents, workspace_diff_file_change, workspace_diff_file_change_type, workspace_diff_mode, workspace_diff_result, workspaces_add_summary_request, workspaces_add_summary_result, workspaces_autopilot_objective_exists_result, workspaces_checkpoints, workspaces_create_directory_request, workspaces_create_file_request, workspaces_delete_autopilot_objective_result, workspaces_diff_request, workspaces_ensure_request, workspaces_get_workspace_result, workspaces_list_checkpoints_result, workspaces_list_files_result, workspaces_read_autopilot_objective_result, workspaces_read_checkpoint_request, workspaces_read_checkpoint_result, workspaces_read_file_request, workspaces_read_file_result, workspaces_remove_path_request, workspaces_rename_path_request, workspaces_save_large_paste_request, workspaces_save_large_paste_result, workspaces_stat_file_request, workspaces_stat_file_result, workspaces_truncate_summaries_request, workspace_summary_host_type, workspaces_update_metadata_request, workspaces_workspace_details_host_type, workspaces_write_autopilot_objective_request, workspaces_write_autopilot_objective_result, connector_session_account_result, session_auth_info_result, session_context_attribution, session_context_info, subagent_settings, task_progress, workspace_summary) + return RPC(abort_request, abort_result, accepted_enqueue_command_result, account_all_users, account_auth_info, account_describe_auth_info_request, account_describe_auth_info_result, account_get_all_users_result, account_get_current_auth_result, account_get_gist_support_request, account_get_gist_support_result, account_get_quota_request, account_get_quota_result, account_get_token_env_var_result, account_get_web_url_request, account_get_web_url_result, account_kind, account_login_request, account_login_result, account_logout_request, account_logout_result, account_quota_snapshot, accounts_enumerate_request, accounts_get_request, accounts_set_request, account_status, adaptive_thinking_support, agent_discovery_path, agent_discovery_path_list, agent_discovery_path_scope, agent_get_current_result, agent_info, agent_info_source, agent_list, agent_list_request, agent_registry_live_target_entry, agent_registry_live_target_entry_attention_kind, agent_registry_live_target_entry_kind, agent_registry_live_target_entry_last_terminal_event, agent_registry_live_target_entry_status, agent_registry_log_capture, agent_registry_log_capture_open_error_reason, agent_registry_spawn_error, agent_registry_spawn_permission_mode, agent_registry_spawn_registry_timeout, agent_registry_spawn_request, agent_registry_spawn_result, agent_registry_spawn_spawned, agent_registry_spawn_validation_error, agent_registry_spawn_validation_error_field, agent_registry_spawn_validation_error_reason, agent_reload_result, agents_custom_agent_initial_model_decision_params, agents_custom_agent_initial_model_decision_result, agents_discover_request, agent_select_request, agent_select_result, agent_set_prompt_request, agents_get_available_builtins_request, agents_get_available_builtins_result, agents_get_builtin_definition_request, agents_get_builtin_definition_result, agents_get_builtin_listing_definition_request, agents_get_builtin_listing_definition_result, agents_get_builtins_result, agents_get_discovery_paths_request, api_key_auth_info, auth_enumerate_query, auth_enumerate_value, auth_identity, auth_identity_metadata, auth_info, auth_info_type, auth_login_account, auth_login_advance_request, auth_login_begin_request, auth_login_begun, auth_login_cancel_request, auth_login_result_dto, auth_login_result_status, auth_login_step, auth_read_query, auth_read_value, auth_status_dto, auth_validation_error, auth_validation_errors, auth_write, auth_write_result, autopilot_objective_credit_limit, autopilot_objective_get_state_result, autopilot_objective_state, autopilot_objective_status, auto_tier_descriptor, auto_tier_metadata, auto_tier_status, builtin_agent_summary, built_in_model_catalog, built_in_model_catalog_entry, builtin_tool_descriptor, builtin_tool_format, builtin_tool_format_type, builtin_tool_input_schema, builtin_tool_input_schema_type, builtin_tool_safe_for_telemetry, builtin_tool_safe_telemetry_fields, cancel_user_requested_shell_command_result, canvas_action, canvas_action_invoke_request, canvas_action_invoke_result, canvas_close_request, canvas_host_context, canvas_host_context_capabilities, canvas_json_schema, canvas_list, canvas_list_open_result, canvas_open_request, canvas_provider_close_request, canvas_provider_invoke_action_request, canvas_provider_open_request, canvas_provider_open_result, canvas_provider_register_request, canvas_provider_unregister_request, canvas_session_context, capi_session_options, card_digest, card_digest_algorithm, card_digest_value, catalog_agent_plugin_candidate, catalog_agent_plugin_candidate_kind, catalog_agent_plugin_candidate_provenance, catalog_agent_plugin_compatibility_tag, catalog_agent_plugin_media_type, catalog_ai_skill_candidate, catalog_ai_skill_candidate_kind, catalog_ai_skill_candidate_provenance, catalog_ai_skill_installability, catalog_ai_skill_media_type, catalog_authentication_required_error, catalog_authentication_required_reason, catalog_candidate, catalog_candidate_kind, catalog_candidate_source, catalog_candidate_source_embedded, catalog_candidate_source_url, catalog_capability, catalog_capability_id, catalog_client_contract, catalog_contract_violation_error, catalog_contract_violation_reason, catalog_handle_rejected_error, catalog_handle_rejection_reason, catalog_handle_type, catalog_invalid_request_error, catalog_invalid_request_field, catalog_malformed_card_error, catalog_malformed_card_reason, catalog_mcp_server_candidate, catalog_mcp_server_candidate_kind, catalog_mcp_server_candidate_provenance, catalog_mcp_server_installability, catalog_media_type, catalog_negotiated_contract, catalog_negotiation_refused_error, catalog_negotiation_refused_reason, catalog_network_failure_error, catalog_network_failure_reason, catalog_not_installable_error, catalog_not_installable_reason, catalog_plugin_repository_source, catalog_policy_rejected_error, catalog_resource_identity, catalog_resource_version, catalog_search_page, catalog_search_pagination, catalog_search_request, catalog_search_result, catalog_search_succeeded, catalog_search_total_count_relation, catalog_selection_cancelled, catalog_selection_decision, catalog_selection_declined, catalog_selection_foreign, catalog_selection_invalid, catalog_selection_replayed, catalog_selection_request, catalog_selection_result, catalog_selection_selected, catalog_selection_stale, catalog_selection_timed_out, catalog_selection_wrong_kind, catalog_trust_eligibility, catalog_trust_provenance, catalog_trust_snapshot, catalog_trust_snapshot_absent, catalog_trust_snapshot_absent_status, catalog_trust_snapshot_current, catalog_trust_snapshot_current_status, catalog_trust_snapshot_downgraded, catalog_trust_snapshot_downgraded_status, catalog_trust_snapshot_malformed, catalog_trust_snapshot_malformed_status, catalog_trust_snapshot_revoked, catalog_trust_snapshot_revoked_status, catalog_trust_snapshot_schema_version, catalog_trust_snapshot_stale, catalog_trust_snapshot_stale_status, catalog_trust_snapshot_unsupported, catalog_trust_snapshot_unsupported_status, catalog_trust_source, catalog_trust_tier, catalog_unavailable_error, catalog_unavailable_reason, catalog_unavailable_transport_error, catalog_unavailable_transport_reason, catalog_unsafe_retrieval_error, catalog_unsafe_retrieval_reason, catalog_unsupported_kind_error, client_metadata, client_task_cancel_reason, client_task_cancel_request, client_task_cancel_result, command_list, commands_finalize_invocation_effect_request, commands_finalize_invocation_effect_result, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_invocation_effect_outcome, commands_invocation_origin, commands_invoke_request, commands_list_request, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, completions_get_trigger_characters_result, completions_request_request, completions_request_result, configure_session_extensions_params, connect_client_info, connected_remote_session_metadata, connected_remote_session_metadata_kind, connected_remote_session_metadata_repository, connector_account_request, connector_authorization_requirement, connector_authorization_scope, connector_availability, connector_capabilities, connector_catalog_entry, connector_catalog_result, connector_catalog_status, connector_connect_request, connector_connect_result, connector_continue_request, connector_disconnect_result, connector_discovery_account, connector_discovery_account_list, connector_discovery_account_request, connector_discovery_auth_info, connector_discovery_availability, connector_discovery_capabilities, connector_discovery_catalog_entry, connector_discovery_catalog_result, connector_mcp_status, connector_reconcile_request, connector_runtime_status, connector_session_account, connector_status, connect_remote_session_params, connect_request, connect_result, content_exclusion_check_paths_request, content_exclusion_check_paths_result, content_exclusion_path_check, content_filter_mode, context_heaviest_message, copilot_api_token_auth_info, copilot_user_response, copilot_user_response_endpoints, copilot_user_response_quota_snapshots, copilot_user_response_quota_snapshots_chat, copilot_user_response_quota_snapshots_completions, copilot_user_response_quota_snapshots_premium_interactions, current_model, current_tool_metadata, customization_reload_outcome, customization_reload_status, customization_reload_subsystem, customizations_reload_result, debug_collect_logs_collected_entry, debug_collect_logs_destination, debug_collect_logs_entry, debug_collect_logs_entry_kind, debug_collect_logs_include, debug_collect_logs_redaction, debug_collect_logs_request, debug_collect_logs_result, debug_collect_logs_result_kind, debug_collect_logs_skipped_entry, debug_collect_logs_source, diagnostic_cursor_status, diagnostic_entry, diagnostic_log_level, diagnostics_configuration, diagnostics_configure_request, diagnostic_severity, diagnostic_source, diagnostic_sources_configuration, diagnostics_read_request, diagnostics_read_result, discovered_canvas, discovered_extension, discovered_extension_mode, discovered_extension_plugin, discovered_extensions, discovered_extensions_disable_request, discovered_extensions_enable_request, discovered_extension_source, discovered_hook, discovered_mcp_server, discovered_mcp_server_type, discovered_model, discovered_model_list, enqueue_command_params, enqueue_command_result, env_auth_info, environment_capabilities, environment_kind, environments_delete_request, environments_delete_result, environments_get_request, environments_get_result, environments_list_request, environments_list_result, event_log_read_request, event_log_release_interest_result, event_log_tail_result, event_log_types, events_agent_scope, events_cursor_status, events_read_direction, events_read_result, execute_command_params, execute_command_result, extension, extension_context_push_input, extension_launch_profile, extension_launch_provider_resolve_request, extension_launch_provider_resolve_result, extension_list, extensions_disable_request, extensions_enable_request, extension_source, extension_status, external_tool_result, external_tool_text_result_for_llm, external_tool_text_result_for_llm_binary_results_for_llm, external_tool_text_result_for_llm_binary_results_for_llm_type, external_tool_text_result_for_llm_content, external_tool_text_result_for_llm_content_audio, external_tool_text_result_for_llm_content_image, external_tool_text_result_for_llm_content_resource, external_tool_text_result_for_llm_content_resource_details, external_tool_text_result_for_llm_content_resource_link, external_tool_text_result_for_llm_content_resource_link_icon, external_tool_text_result_for_llm_content_resource_link_icon_theme, external_tool_text_result_for_llm_content_shell_exit, external_tool_text_result_for_llm_content_terminal, external_tool_text_result_for_llm_content_text, feature_flag_availability, feature_flag_metadata, feature_flags_metadata_result, feature_flags_resolve_request, feature_flags_resolve_result, feature_flags_resolve_startup_gates_request, feature_flags_resolve_startup_gates_result, filter_mapping, fleet_start_request, fleet_start_result, folder_trust_add_params, folder_trust_check_params, folder_trust_check_result, folder_trust_is_trusted_params, force_login_orgs_decision, gh_cli_auth_info, git_current_branch_remote_result, git_cwd_request, git_hub_environment, git_hub_owner_option, git_hub_owners_cancel_request, git_hub_owners_cancel_result, git_hub_owners_list_request, git_hub_owners_list_result, git_hub_owners_request_id_result, git_hub_repository_at_path_request, git_hub_repository_at_path_result, git_hub_repository_identity, git_hub_telemetry_client_info, git_hub_telemetry_event, git_hub_telemetry_notification, git_hub_token_acquire_reason, git_hub_token_acquire_request, git_hub_token_acquire_result, git_remote_repository, git_repos_from_remotes_request, git_repos_from_remotes_result, global_state_load_for_config_dir_request, global_state_load_result, global_state_write_key_request, handle_pending_tool_call_request, handle_pending_tool_call_result, history_abort_manual_compaction_result, history_cancel_background_compaction_result, history_clear_context_request, history_clear_context_result, history_compact_context_window, history_compact_request, history_compact_result, history_file_restore_skip_reason, history_list_rewind_points_result, history_preview_rewind_request, history_preview_rewind_result, history_rewind_change_type, history_rewind_file_preview, history_rewind_mode, history_rewind_outcome, history_rewind_point, history_rewind_request, history_rewind_result, history_rewind_unavailable_reason, history_skipped_file_restore, history_summarize_for_handoff_result, history_truncate_request, history_truncate_result, hmac_auth_info, hook_invoke_request, hook_invoke_response, hook_origin, hooks_discover_request, hooks_discover_result, hook_type, host_configuration, host_dispose_request, host_empty_result, host_environment_credentials, host_exited_notification, host_exit_reason, host_git_hub_environment_options, host_list_sessions_request, host_list_sessions_result, host_local_server_configuration, host_local_server_options, host_publish_session_request, host_publish_session_result, host_ready_request, host_register_session_request, host_session_create_callback, host_session_create_request, host_session_create_result, host_session_released_notification, host_session_release_request, host_session_summary, host_start_request, host_start_result, installation_catalogue_identity, installation_confirmation_request, installation_confirmation_response, installation_decision, installation_review, installed_plugin, installed_plugin_info, installed_plugin_source, installed_plugin_source_git_hub, installed_plugin_source_local, installed_plugin_source_url, instruction_discovery_path, instruction_discovery_path_kind, instruction_discovery_path_list, instruction_discovery_path_location, instructions_discover_repo_sources_request, instructions_discover_request, instructions_get_discovery_paths_request, instructions_get_sources_result, instructions_init_prompt_result, instruction_source, instruction_source_location, instruction_source_type, interrupt_main_turn_request, interrupt_main_turn_result, json_schema_response_format, llm_inference_headers, llm_inference_http_request_chunk_request, llm_inference_http_request_chunk_result, llm_inference_http_request_start_request, llm_inference_http_request_start_result, llm_inference_http_request_start_transport, llm_inference_http_response_chunk_error, llm_inference_http_response_chunk_request, llm_inference_http_response_chunk_result, llm_inference_http_response_start_request, llm_inference_http_response_start_result, llm_inference_set_provider_result, local_session_metadata_value, location_permissions_cleanup_result, logged_in_user, login_provider_kind, log_request, log_result, lsp_initialize_request, managed_mcp_server_config, managed_permission_evaluation, managed_permission_operation, managed_permission_operation_kind, managed_permissions_evaluate_request, managed_permissions_evaluate_result, managed_permission_verdict, managed_plugin_retry_entry, managed_plugin_retry_status, managed_setting_meta, managed_settings_apply_controls_params, managed_settings_apply_controls_result, managed_settings_bypass_disable_source, managed_settings_bypass_policy_resolution, managed_settings_channel, managed_settings_compose_layer, managed_settings_compose_request, managed_settings_compose_result, managed_settings_diagnostic, managed_settings_diagnostic_severity, managed_settings_ingest_params, managed_settings_ingest_result, managed_settings_layer, managed_settings_load_remote_control_params, managed_settings_load_remote_control_result, managed_settings_log_level, managed_settings_log_message, managed_settings_meta, managed_settings_parse_client_value_params, managed_settings_parse_client_value_result, managed_settings_policy_helper_authority, managed_settings_policy_helper_reason, managed_settings_read_device_result, managed_settings_read_result, managed_settings_resolve_bypass_disable_transition_params, managed_settings_resolve_bypass_disable_transition_result, managed_settings_resolved_data, managed_settings_resolve_device_force_login_orgs_gate_params, managed_settings_resolve_effective_value_params, managed_settings_resolve_effective_value_result, managed_settings_resolve_request, managed_settings_resolve_result, managed_settings_resolve_startup_policy_params, managed_settings_resolve_startup_policy_result, managed_settings_retain_policy_params, managed_settings_schema_result, managed_settings_server_policy_cache_mode, managed_settings_server_policy_resolution, managed_settings_validate_request, managed_settings_validate_result, managed_settings_values, marketplace_add_result, marketplace_browse_result, marketplace_info, marketplace_list_result, marketplace_plugin_info, marketplace_refresh_entry, marketplace_refresh_result, marketplace_remove_result, mcp_allowed_server, mcp_apply_install_request, mcp_apply_uninstall_request, mcp_apps_call_tool_request, mcp_apps_diagnose_capability, mcp_apps_diagnose_request, mcp_apps_diagnose_result, mcp_apps_diagnose_server, mcp_apps_host_context, mcp_apps_host_context_details, mcp_apps_host_context_details_available_display_mode, mcp_apps_host_context_details_display_mode, mcp_apps_host_context_details_platform, mcp_apps_host_context_details_theme, mcp_apps_list_tools_request, mcp_apps_list_tools_result, mcp_apps_read_resource_request, mcp_apps_read_resource_result, mcp_apps_resource_content, mcp_apps_set_host_context_details, mcp_apps_set_host_context_details_available_display_mode, mcp_apps_set_host_context_details_display_mode, mcp_apps_set_host_context_details_platform, mcp_apps_set_host_context_details_theme, mcp_apps_set_host_context_request, mcp_cancel_sampling_execution_params, mcp_cancel_sampling_execution_result, mcp_config_add_request, mcp_config_disable_request, mcp_config_enable_request, mcp_config_list, mcp_config_remove_request, mcp_config_update_request, mcp_configured_server, mcp_configured_server_list, mcp_configured_server_state, mcp_configure_git_hub_request, mcp_configure_git_hub_result, mcp_diagnostic_details, mcp_diagnostic_direction, mcp_diagnostic_kind, mcp_diagnostic_source_configuration, mcp_disable_request, mcp_discover_request, mcp_discover_result, mcp_elicitation_form_mode, mcp_enable_request, mcp_execute_sampling_params, mcp_execute_sampling_request, mcp_execute_sampling_result, mcp_failed_server, mcp_filtered_server, mcp_headers_handle_pending_headers_refresh_request, mcp_headers_handle_pending_headers_refresh_request_request, mcp_headers_handle_pending_headers_refresh_request_result, mcp_host_state, mcp_installation_failure_reason, mcp_installation_input, mcp_installation_management_outcome, mcp_installation_management_result, mcp_installation_operation_request, mcp_installation_operation_status, mcp_installation_outcome, mcp_installation_remote_configuration, mcp_installation_result, mcp_installation_review, mcp_installation_secret, mcp_installation_secret_storage, mcp_installations_request, mcp_installation_state, mcp_installation_summary, mcp_install_plan, mcp_is_server_running_request, mcp_is_server_running_result, mcp_list_builtin_server_names_result, mcp_list_tools_request, mcp_list_tools_result, mcp_oauth_authentication_state_changed_request, mcp_oauth_cancel_login_request, mcp_oauth_cancel_login_result, mcp_oauth_complete_request, mcp_oauth_handle_pending_request, mcp_oauth_handle_pending_result, mcp_oauth_login_grant_type, mcp_oauth_login_request, mcp_oauth_login_result, mcp_oauth_pending_request_response, mcp_oauth_prepare_login_request, mcp_oauth_prepare_login_result, mcp_oauth_probe_needs_auth_reason, mcp_oauth_probe_request, mcp_oauth_probe_result, mcp_oauth_respond_request, mcp_oauth_respond_result, mcp_owned_oauth_login_status, mcp_plan_configuration_change, mcp_plan_configuration_operation, mcp_plan_enum_value_type, mcp_plan_install_planned, mcp_plan_install_request, mcp_plan_install_result, mcp_plan_install_source, mcp_plan_install_source_candidate, mcp_plan_install_source_candidate_kind, mcp_plan_install_source_card, mcp_plan_install_source_card_kind, mcp_plan_package_install_method, mcp_plan_package_transport, mcp_plan_policy_decision, mcp_plan_policy_result, mcp_plan_policy_source, mcp_plan_provenance, mcp_plan_remote_install_method, mcp_plan_remote_transport, mcp_plan_required_value, mcp_plan_required_value_enum, mcp_plan_required_value_enum_kind, mcp_plan_required_value_scalar, mcp_plan_required_value_scalar_kind, mcp_plan_resource_identity, mcp_plan_scalar_value_type, mcp_plan_scope, mcp_plan_secret_placeholder, mcp_plan_secret_reference, mcp_plan_target, mcp_plan_transport_choice, mcp_plan_transport_choice_package, mcp_plan_transport_choice_remote, mcp_plan_uninstall_request, mcp_plan_value_category, mcp_prepared_install, mcp_prepare_install_request, mcp_prompt, mcp_prompt_argument, mcp_prompt_icon, mcp_prompt_message, mcp_prompt_role, mcp_prompts_get_request, mcp_prompts_get_result, mcp_prompts_list_request, mcp_prompts_list_result, mcp_register_external_client_request, mcp_registry_cancel_request, mcp_registry_cancel_result, mcp_registry_request_id_result, mcp_registry_search_request, mcp_registry_search_result, mcp_reload_config, mcp_reload_with_config_request, mcp_remove_git_hub_result, mcp_resource, mcp_resource_annotations, mcp_resource_content, mcp_resource_icon, mcp_resources_list_request, mcp_resources_list_result, mcp_resources_list_templates_request, mcp_resources_list_templates_result, mcp_resources_read_request, mcp_resources_read_result, mcp_resource_template, mcp_restart_server_request, mcp_safe_for_telemetry, mcp_safe_for_telemetry_fields, mcp_sampling_execution_action, mcp_sampling_execution_result, mcp_serializable_server_config, mcp_server, mcp_server_auth_config, mcp_server_auth_config_redirect_port, mcp_server_card_embedded, mcp_server_card_embedded_kind, mcp_server_card_media_type, mcp_server_card_reference, mcp_server_card_url, mcp_server_card_url_kind, mcp_server_config, mcp_server_config_defer_tools, mcp_server_config_http, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_memory, mcp_server_config_memory_type, mcp_server_config_stdio, mcp_server_config_stdio_type, mcp_server_failure_info, mcp_server_list, mcp_server_needs_auth_info, mcp_server_ownership, mcp_set_env_value_mode_details, mcp_set_env_value_mode_params, mcp_set_env_value_mode_result, mcp_should_exclude_git_hub_tools_request, mcp_should_exclude_git_hub_tools_result, mcp_source_file, mcp_source_plugin, mcp_source_ref, mcp_start_server_request, mcp_start_servers_result, mcp_stop_server_request, mcp_task_metadata, mcp_tools, mcp_tool_ui, mcp_tool_ui_visibility, mcp_uninstall_plan, mcp_unregister_external_client_request, memory_configuration, metadata_context_attribution_result, metadata_context_heaviest_messages_request, metadata_context_heaviest_messages_result, metadata_context_info_request, metadata_context_info_result, metadata_is_processing_result, metadata_recompute_context_tokens_request, metadata_recompute_context_tokens_result, metadata_record_context_change_request, metadata_record_context_change_result, metadata_set_working_directory_request, metadata_set_working_directory_result, metadata_snapshot_current_mode, metadata_snapshot_remote_metadata, metadata_snapshot_remote_metadata_repository, metadata_snapshot_remote_metadata_task_type, metadata_update_client_metadata_request, model, model_apply_startup_overlay_request, model_artifact_details, model_billing, model_billing_promo, model_billing_token_prices, model_billing_token_prices_long_context, model_capabilities, model_capabilities_limits, model_capabilities_limits_vision, model_capabilities_override, model_capabilities_override_limits, model_capabilities_override_limits_vision, model_capabilities_override_supports, model_capabilities_supports, model_clear_startup_seed_request, model_clear_startup_seed_result, model_get_reasoning_summaries_request, model_get_reasoning_summaries_result, model_list, model_list_request, model_message, model_picker_category, model_picker_persistence_request, model_picker_price_category, model_picker_settings_context, model_policy, model_policy_state, model_provider_adapter_catalog, model_provider_adapter_descriptor, model_provider_adapter_operation_descriptor, model_provider_attribution, model_provider_automatic_discovery_mode, model_provider_automatic_discovery_policy, model_provider_configuration_disposition, model_provider_configuration_plan, model_provider_descriptor, model_provider_discover_request, model_provider_discover_result, model_provider_discovery_network_scope, model_provider_get_status_request, model_provider_instance, model_provider_instance_reference, model_provider_models_list_request, model_provider_operation_outcome, model_provider_operation_outcome_code, model_provider_prepare_configuration_request, model_provider_provenance, model_provider_provenance_source, model_provider_status, model_provider_warning, model_set_allowed_models_request, model_set_allowed_models_result, model_set_reasoning_effort_request, model_set_reasoning_effort_result, models_list_request, model_switch_auto_tier_request, model_switch_auto_tier_result, model_switch_auto_tier_status, model_switch_confirmation, model_switch_to_request, model_switch_to_result, model_warning_text, mode_set_request, mode_set_result, move_mcp_loading_to_background_result, named_provider_config, name_get_result, name_set_auto_request, name_set_auto_result, name_set_request, open_canvas_instance, options_update_additional_content_exclusion_policy, options_update_additional_content_exclusion_policy_rule, options_update_additional_content_exclusion_policy_rule_source, options_update_additional_content_exclusion_policy_scope, options_update_context_tier, options_update_env_value_mode, options_update_reasoning_summary, options_update_tool_filter_precedence, pending_external_tool_request, pending_external_tool_request_list, pending_permission_request, pending_permission_request_list, permission_decision, permission_decision_approved, permission_decision_approved_for_location, permission_decision_approved_for_session, permission_decision_approve_for_location, permission_decision_approve_for_location_approval, permission_decision_approve_for_location_approval_commands, permission_decision_approve_for_location_approval_custom_tool, permission_decision_approve_for_location_approval_extension_env_access, permission_decision_approve_for_location_approval_extension_management, permission_decision_approve_for_location_approval_extension_permission_access, permission_decision_approve_for_location_approval_mcp, permission_decision_approve_for_location_approval_mcp_sampling, permission_decision_approve_for_location_approval_memory, permission_decision_approve_for_location_approval_read, permission_decision_approve_for_location_approval_workflow, permission_decision_approve_for_location_approval_write, permission_decision_approve_for_session, permission_decision_approve_for_session_approval, permission_decision_approve_for_session_approval_commands, permission_decision_approve_for_session_approval_custom_tool, permission_decision_approve_for_session_approval_extension_env_access, permission_decision_approve_for_session_approval_extension_management, permission_decision_approve_for_session_approval_extension_permission_access, permission_decision_approve_for_session_approval_mcp, permission_decision_approve_for_session_approval_mcp_sampling, permission_decision_approve_for_session_approval_memory, permission_decision_approve_for_session_approval_read, permission_decision_approve_for_session_approval_workflow, permission_decision_approve_for_session_approval_write, permission_decision_approve_once, permission_decision_approve_permanently, permission_decision_approve_read_only_for_session, permission_decision_cancelled, permission_decision_context, permission_decision_denied_by_content_exclusion_policy, permission_decision_denied_by_permission_request_hook, permission_decision_denied_by_rules, permission_decision_denied_interactively_by_user, permission_decision_denied_no_approval_rule_and_could_not_request_from_user, permission_decision_outcome, permission_decision_reject, permission_decision_request, permission_decision_surface, permission_decision_user_not_available, permission_location_add_tool_approval_params, permission_location_apply_params, permission_location_apply_result, permission_location_resolve_params, permission_location_resolve_result, permission_location_type, permission_mode_source, permission_paths_add_params, permission_paths_allowed_check_params, permission_paths_allowed_check_result, permission_paths_config, permission_paths_list, permission_paths_update_primary_params, permission_paths_workspace_check_params, permission_paths_workspace_check_result, permission_prompt_shown_notification, permission_request_result, permission_response_capability, permission_rules_set, permissions_configure_additional_content_exclusion_policy, permissions_configure_additional_content_exclusion_policy_rule, permissions_configure_additional_content_exclusion_policy_rule_source, permissions_configure_additional_content_exclusion_policy_scope, permissions_configure_params, permissions_configure_result, permissions_evaluate_managed_policy_params, permissions_evaluate_managed_policy_result, permissions_folder_trust_add_trusted_result, permissions_get_mode_request, permissions_get_mode_result, permissions_locations_add_tool_approval_details, permissions_locations_add_tool_approval_details_commands, permissions_locations_add_tool_approval_details_custom_tool, permissions_locations_add_tool_approval_details_extension_env_access, permissions_locations_add_tool_approval_details_extension_management, permissions_locations_add_tool_approval_details_extension_permission_access, permissions_locations_add_tool_approval_details_mcp, permissions_locations_add_tool_approval_details_mcp_sampling, permissions_locations_add_tool_approval_details_memory, permissions_locations_add_tool_approval_details_read, permissions_locations_add_tool_approval_details_workflow, permissions_locations_add_tool_approval_details_write, permissions_locations_add_tool_approval_result, permissions_modify_rules_params, permissions_modify_rules_result, permissions_modify_rules_scope, permissions_notify_prompt_shown_result, permissions_paths_add_result, permissions_paths_list_request, permissions_paths_update_primary_result, permissions_pending_requests_request, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_approve_all_request, permissions_set_approve_all_result, permissions_set_approve_all_source, permissions_set_mode_request, permissions_set_mode_result, permissions_set_required_request, permissions_set_required_result, permissions_urls_set_unrestricted_mode_result, permission_urls_config, permission_urls_set_unrestricted_mode_params, persistence_write_raw_json_locked_request, persistence_write_raw_json_locked_result, ping_request, ping_result, plan_read_result, plan_read_sql_todos_result, plan_read_sql_todos_with_dependencies_result, plan_sql_todo_dependency, plan_sql_todos_row, plan_update_request, plugin, plugin_install_result, plugin_install_staging_mode, plugin_list, plugin_list_result, plugins_builtin_set_request, plugins_disable_request, plugins_enable_request, plugins_install_request, plugins_marketplaces_add_request, plugins_marketplaces_browse_request, plugins_marketplaces_refresh_request, plugins_marketplaces_remove_request, plugins_reload_request, plugins_uninstall_request, plugins_update_request, plugin_update_all_entry, plugin_update_all_result, plugin_update_result, protocol_append_mode, protocol_customize_mode, protocol_external_tool_defer, protocol_external_tool_definition, protocol_marker_section_override, protocol_replace_mode, protocol_section_override, protocol_static_section_action, protocol_static_section_override, protocol_system_message_append_config, protocol_system_message_config, protocol_system_message_customize_config, protocol_system_message_replace_config, provider_add_request, provider_add_result, provider_config, provider_config_azure, provider_config_model_provider, provider_config_transport, provider_config_type, provider_config_wire_api, provider_descriptor, provider_endpoint, provider_endpoint_transport, provider_endpoint_type, provider_endpoint_wire_api, provider_get_endpoint_request, provider_model_config, provider_session_token, provider_sync_request, provider_sync_result, provider_token_acquire_request, provider_token_acquire_result, provider_withdraw_request, provider_withdraw_result, push_attachment, push_attachment_blob, push_attachment_directory, push_attachment_file, push_attachment_file_line_range, push_attachment_git_hub_actions_job, push_attachment_git_hub_commit, push_attachment_git_hub_file, push_attachment_git_hub_file_diff, push_attachment_git_hub_file_diff_side, push_attachment_git_hub_reference, push_attachment_git_hub_reference_type, push_attachment_git_hub_release, push_attachment_git_hub_repository, push_attachment_git_hub_snippet, push_attachment_git_hub_tree_comparison, push_attachment_git_hub_tree_comparison_side, push_attachment_git_hub_url, push_attachment_selection, push_attachment_selection_details, push_attachment_selection_details_end, push_attachment_selection_details_start, push_git_hub_repo_ref, queue_append_steering_request, queue_begin_deferred_idle_drain_request, queue_begin_deferred_idle_drain_result, queue_consume_system_notifications_request, queued_command_handled, queued_command_not_handled, queued_command_result, queue_defer_session_idle_request, queue_duplicate_at_request, queue_duplicate_at_result, queue_enqueue_resume_pending_result, queue_finish_deferred_idle_drain_request, queue_finish_deferred_idle_drain_result, queue_has_pending_result, queue_insert_at_request, queue_insert_at_result, queue_insert_message, queue_move_item_request, queue_move_item_result, queue_pending_items, queue_pending_items_kind, queue_pending_items_result, queue_remove_at_request, queue_remove_at_result, queue_remove_most_recent_result, queue_send_now_request, queue_send_now_result, queue_set_drain_paused_request, queue_snapshot_result, queue_update_text_request, queue_update_text_result, queue_withdraw_message_request, queue_withdraw_message_result, quota_take_warnings_result, quota_warning_projection, register_event_interest_params, register_event_interest_result, release_event_interest_params, remote_control_config, remote_control_config_existing_mc_session, remote_control_status, remote_control_status_active, remote_control_status_connecting, remote_control_status_error, remote_control_status_off, remote_control_status_result, remote_control_stop_result, remote_control_transfer_result, remote_enable_request, remote_enable_result, remote_notify_steerable_changed_request, remote_notify_steerable_changed_result, remote_session_connection_result, remote_session_host_status, remote_session_metadata_repository, remote_session_metadata_task_type, remote_session_metadata_value, remote_session_mode, remote_session_repository, repo_settings_edit_request, repo_settings_edit_scope, repo_settings_read_raw_request, repo_settings_read_raw_result, repo_settings_write_all_request, repo_settings_write_key_request, response_format, sandbox_apply_defaults_request, sandbox_check_enforcement_command_result, sandbox_check_platform_support_result, sandbox_check_windows_host_loopback_support_result, sandbox_config, sandbox_config_auth, sandbox_config_source, sandbox_config_user_policy, sandbox_config_user_policy_experimental, sandbox_config_user_policy_experimental_seatbelt, sandbox_config_user_policy_filesystem, sandbox_config_user_policy_network, sandbox_config_user_policy_network_proxy, sandbox_config_user_policy_seatbelt, sandbox_credentials_config, sandbox_credential_suggestion, sandbox_credential_suggestions_result, sandbox_disable_for_session_request, sandbox_disable_for_session_result, sandbox_docs_url_result, sandbox_effective_for_optional_params, sandbox_effective_for_optional_result, sandbox_enforcement_command_allowed_params, sandbox_enforcement_status, sandbox_filter_picker_candidates_params, sandbox_filter_picker_candidates_result, sandbox_get_unsupported_message_result, sandbox_get_unsupported_setting_disabled_notice_result, sandbox_grant_path_for_request_request, sandbox_grant_path_for_request_result, sandbox_host_capability, sandbox_host_capability_name, sandbox_host_support, sandbox_masked_env_var, sandbox_picker_candidate, sandbox_prepare_picker_policy_params, sandbox_prepare_picker_policy_result, sandbox_proxy_ca_check_installation_support_result, sandbox_proxy_ca_check_relevance_result, sandbox_proxy_ca_create_result, sandbox_proxy_ca_request, sandbox_proxy_ca_state, sandbox_proxy_ca_status, sandbox_proxy_secret_get_result, sandbox_proxy_secret_location, sandbox_proxy_secret_save_request, sandbox_proxy_secret_save_result, sandbox_resolve_floored_config_params, sandbox_resolve_floored_config_result, sandbox_session_change, sandbox_settings_apply_defaults_result, sandbox_unsupported_startup_notice_params, sandbox_unsupported_startup_notice_result, schedule_add_at_request, schedule_add_cron_request, schedule_add_request, schedule_add_result, schedule_add_self_paced_request, schedule_entry, schedule_has_self_paced_result, schedule_list, schedule_rearm_self_paced_request, schedule_stop_request, schedule_stop_result, secrets_add_filter_values_request, secrets_add_filter_values_result, send_agent_mode, send_attachments_to_message_params, send_message_item, send_messages_request, send_messages_result, send_mode, send_request, send_result, send_system_notification_request, server_agent_list, server_instruction_source_list, server_skill, server_skill_list, session_activity, session_agent_list_request, session_auth_login_request, session_auth_logout_user_request, session_auth_status, session_auth_switch_request, session_bulk_delete_result, session_cancel_all_background_agents_result, session_capability, session_commands_list_request, session_completion_item, session_connected_ide_info, session_context, session_context_host_type, session_enrich_metadata_result, session_fs_append_file_request, session_fs_error, session_fs_error_code, session_fs_exists_request, session_fs_exists_result, session_fs_mkdir_request, session_fs_readdir_request, session_fs_readdir_result, session_fs_readdir_with_types_entry, session_fs_readdir_with_types_entry_type, session_fs_readdir_with_types_request, session_fs_readdir_with_types_result, session_fs_read_file_bytes_request, session_fs_read_file_bytes_result, session_fs_read_file_request, session_fs_read_file_result, session_fs_rename_request, session_fs_rm_request, session_fs_set_provider_capabilities, session_fs_set_provider_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_sqlite_exists_request, session_fs_sqlite_exists_result, session_fs_sqlite_query_request, session_fs_sqlite_query_result, session_fs_sqlite_query_type, session_fs_sqlite_transaction_error, session_fs_sqlite_transaction_error_class, session_fs_sqlite_transaction_request, session_fs_sqlite_transaction_result, session_fs_sqlite_transaction_statement, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_bytes_request, session_fs_write_file_request, session_git_hub_auth_get_all_auth_available_result, session_git_hub_auth_logout_result, session_git_hub_auth_logout_user_result, session_history_compact_request, session_installed_plugin, session_installed_plugin_source, session_installed_plugin_source_git_hub, session_installed_plugin_source_local, session_installed_plugin_source_url, session_limit_prediction_baseline_data, session_limit_prediction_client_type, session_limit_prediction_details, session_limit_prediction_predict_request, session_limit_prediction_request, session_limit_prediction_result, session_limit_prediction_source, session_limit_prediction_tier, session_limit_prediction_tier_option, session_limit_prediction_unavailable_reason, session_list, session_list_entry, session_list_filter, session_load_deferred_repo_hooks_result, session_log_level, session_managed_permissions, session_managed_settings, session_mcp_apps_call_tool_result, session_mcp_oauth_cancel_login_request, session_mcp_oauth_cancel_login_result, session_mcp_oauth_prepare_login_request, session_mcp_oauth_prepare_login_result, session_mcp_set_connected_ide_info_params, session_metadata_snapshot, session_mode, session_model_clear_startup_seed_request, session_model_clear_startup_seed_result, session_model_list, session_model_list_request, session_model_price_category, session_open_options, session_open_options_additional_content_exclusion_policy, session_open_options_additional_content_exclusion_policy_rule, session_open_options_additional_content_exclusion_policy_rule_source, session_open_options_additional_content_exclusion_policy_scope, session_open_options_env_value_mode, session_open_options_reasoning_summary, session_open_params, session_open_result, session_plugins_disable_request, session_plugins_enable_request, session_plugins_install_request, session_plugins_marketplaces_refresh_request, session_plugins_reload_request, session_plugins_retry_managed_request, session_plugins_retry_managed_result, session_provider_get_endpoint_request, session_prune_result, session_quota_delegate_warning, session_quota_get_result, session_quota_plan_tier, session_quota_projection, session_quota_refresh_result, session_quota_snapshot, session_quota_take_warnings_result, sessions_bulk_delete_request, sessions_check_in_use_request, sessions_check_in_use_result, sessions_client_metadata_entry, sessions_close_request, sessions_close_result, sessions_create_workspace_request, sessions_create_workspace_result, sessions_delete_request, sessions_enrich_metadata_request, session_set_credentials_params, session_set_credentials_result, session_settings_built_in_tool_availability_snapshot, session_settings_evaluate_predicate_request, session_settings_evaluate_predicate_result, session_settings_job_snapshot, session_settings_model_snapshot, session_settings_online_evaluation_snapshot, session_settings_predicate_name, session_settings_repo_snapshot, session_settings_snapshot, session_settings_validation_snapshot, sessions_find_by_prefix_request, sessions_find_by_prefix_result, sessions_find_by_task_id_request, sessions_find_by_task_id_result, sessions_fork_request, sessions_fork_result, sessions_get_board_entry_count_request, sessions_get_board_entry_count_result, sessions_get_client_metadata_request, sessions_get_client_metadata_result, sessions_get_event_file_path_request, sessions_get_event_file_path_result, sessions_get_last_for_context_request, sessions_get_last_for_context_result, sessions_get_metadata_request, sessions_get_metadata_result, sessions_get_persisted_remote_steerable_request, sessions_get_persisted_remote_steerable_result, session_sizes, sessions_list_non_empty_session_ids_request, sessions_list_non_empty_session_ids_result, sessions_list_request, sessions_load_deferred_repo_hooks_request, sessions_load_workspace_request, sessions_load_workspace_result, sessions_open_attach, sessions_open_cloud, sessions_open_create, sessions_open_handoff, sessions_open_handoff_task_type, sessions_open_progress, sessions_open_progress_status, sessions_open_progress_step, sessions_open_remote, sessions_open_resume, sessions_open_resume_last, sessions_open_status, session_source, sessions_prune_old_request, sessions_read_persisted_events_request, sessions_release_lock_request, sessions_release_lock_result, sessions_reload_plugin_hooks_request, sessions_reload_plugin_hooks_result, sessions_save_request, sessions_save_result, sessions_set_additional_plugins_request, sessions_set_additional_plugins_result, sessions_set_remote_control_steering_request, sessions_start_remote_control_request, sessions_stop_remote_control_request, sessions_transfer_remote_control_request, sessions_update_workspace_fields_request, sessions_update_workspace_fields_result, session_telemetry_engagement, session_update_options_params, session_update_options_result, session_usage_set_code_changes_request, session_visibility_status, session_workflow_pause_at_checkpoint_result, session_working_directory_context, session_working_directory_context_host_type, session_working_directory_context_with_client, settable_auth_info, settable_token_auth_info, settings_edit_operation, settings_edit_result, settings_edit_status, shell_cancel_user_requested_request, shell_credentials, shell_exec_request, shell_exec_result, shell_execute_user_requested_request, shell_init_profile, shell_init_script, shell_init_script_shell, shell_kill_request, shell_kill_result, shell_kill_signal, shell_options, shutdown_request, skill, skill_apply_install_request, skill_apply_uninstall_request, skill_discovery_path, skill_discovery_path_list, skill_discovery_scope, skill_installation_failure_reason, skill_installation_file_review, skill_installation_location, skill_installation_management_outcome, skill_installation_management_result, skill_installation_operation_request, skill_installation_operation_status, skill_installation_outcome, skill_installation_ownership_state, skill_installation_result, skill_installation_review, skill_installation_scope, skill_installation_session_state, skill_installation_source, skill_installations_request, skill_installation_summary, skill_install_plan, skill_list, skill_plan_install_request, skill_plan_uninstall_request, skill_provider_descriptor, skill_provider_list_request, skill_provider_list_result, skill_provider_read_request, skill_provider_read_result, skills_config_set_disabled_skills_request, skills_config_set_skill_disabled_request, skills_disable_request, skills_discover_request, skills_enable_request, skill_set_enabled_request, skills_get_discovery_paths_request, skills_get_invoked_result, skills_invoked_skill, skills_load_diagnostics, skill_uninstall_plan, slash_command_add_timeline_entry_result, slash_command_agent_prompt_result, slash_command_completed_result, slash_command_info, slash_command_input, slash_command_input_choice, slash_command_input_completion, slash_command_invocation_result, slash_command_kind, slash_command_model_picker_dialog, slash_command_select_subcommand_option, slash_command_select_subcommand_result, slash_command_set_model_result, slash_command_set_plan_model_result, slash_command_show_dialog_result, slash_command_text_result, slash_command_timeline_entry, staff_log_level_migration_status, staff_update_channel_migration_status, subagent_settings_entry, subagent_settings_entry_context_tier, system_message_block, task_agent_info, task_agent_progress, task_client_active_status, task_client_execution_mode, task_client_info, task_client_owner, task_client_owner_kind, task_client_owner_presence, task_client_progress, task_client_status, task_client_type, task_client_update, task_complete_data, task_completion_decision, task_execution_mode, task_info, task_kind, task_list, task_progress_line, tasks_cancel_request, tasks_cancel_result, tasks_get_current_promotable_result, tasks_get_progress_request, tasks_get_progress_result, task_shell_info, task_shell_info_attachment_mode, task_shell_progress, tasks_promote_current_to_background_result, tasks_promote_to_background_request, tasks_promote_to_background_result, tasks_refresh_result, tasks_register_request, tasks_register_result, tasks_remove_request, tasks_remove_result, tasks_send_message_request, tasks_send_message_result, tasks_start_agent_request, tasks_start_agent_result, task_status, tasks_update_request, tasks_update_result, tasks_wait_for_pending_result, telemetry_set_feature_overrides_request, token_auth_info, token_provider_auth_info, tool, tool_list, tool_result, tool_result_expanded, tool_result_new_message, tool_result_type, tools_execute_request, tools_get_builtin_descriptors_request, tools_get_builtin_descriptors_result, tools_get_current_metadata_result, tools_initialize_and_validate_result, tools_list_pending_requests_request, tools_list_request, tools_set_request, tools_set_result, tools_shell_descriptor_config, tools_task_complete_event_data_request, tools_update_subagent_settings_result, ui_auto_mode_switch_response, ui_elicitation_array_any_of_field, ui_elicitation_array_any_of_field_items, ui_elicitation_array_any_of_field_items_any_of, ui_elicitation_array_enum_field, ui_elicitation_array_enum_field_items, ui_elicitation_field_value, ui_elicitation_request, ui_elicitation_response, ui_elicitation_response_action, ui_elicitation_response_content, ui_elicitation_result, ui_elicitation_schema, ui_elicitation_schema_property, ui_elicitation_schema_property_boolean, ui_elicitation_schema_property_number, ui_elicitation_schema_property_number_type, ui_elicitation_schema_property_string, ui_elicitation_schema_property_string_format, ui_elicitation_string_enum_field, ui_elicitation_string_one_of_field, ui_elicitation_string_one_of_field_one_of, ui_ephemeral_query_request, ui_ephemeral_query_result, ui_exit_plan_mode_action, ui_exit_plan_mode_response, ui_handle_pending_auto_mode_switch_request, ui_handle_pending_elicitation_request, ui_handle_pending_exit_plan_mode_request, ui_handle_pending_result, ui_handle_pending_sampling_request, ui_handle_pending_sampling_response, ui_handle_pending_session_limits_exhausted_request, ui_handle_pending_user_input_request, ui_register_direct_auto_mode_switch_handler_result, ui_session_limits_exhausted_response, ui_session_limits_exhausted_response_action, ui_unregister_direct_auto_mode_switch_handler_request, ui_unregister_direct_auto_mode_switch_handler_result, ui_user_input_response, unsupported_enqueue_command_result, update_subagent_settings_request, usage_get_metrics_result, usage_set_code_changes_request, user_auth_info, user_requested_shell_command_result, user_setting_metadata, user_settings_apply_top_key_request, user_settings_apply_top_key_result, user_settings_build_runtime_request, user_settings_build_runtime_result, user_settings_edit_request, user_settings_get_paths_request, user_settings_get_paths_result, user_settings_get_result, user_settings_governance_keys_result, user_settings_host_context, user_settings_load_and_merge_repo_request, user_settings_load_and_merge_repo_result, user_settings_load_for_config_dir_request, user_settings_load_for_config_dir_result, user_settings_load_for_edit_result, user_settings_load_repo_request, user_settings_load_repo_result, user_settings_load_with_warning_result, user_settings_merge_managed_and_user_params, user_settings_merge_managed_and_user_result, user_settings_merge_repo_value_params, user_settings_merge_repo_value_result, user_settings_migrate_obsolete_on_disk_request, user_settings_migrate_obsolete_on_disk_result, user_settings_migrate_obsolete_on_disk_status, user_settings_migrate_staff_log_level_once_request, user_settings_migrate_staff_log_level_once_result, user_settings_migrate_staff_update_channel_once_result, user_settings_normalize_request, user_settings_normalize_result, user_settings_path_result, user_settings_problem, user_settings_problem_location, user_settings_problem_source, user_settings_problems_request, user_settings_problems_result, user_settings_reset_staff_model_selection_once_request, user_settings_reset_staff_model_selection_once_result, user_settings_set_hook_disabled_request, user_settings_set_hook_disabled_result, user_settings_set_request, user_settings_unknown_keys_warning_request, user_settings_unknown_keys_warning_result, user_settings_update_request, user_settings_update_result, user_settings_validate_request, user_settings_validate_result, user_settings_validation_issue, user_settings_write_all_request, visibility_get_result, visibility_set_request, visibility_set_result, workflow_abort_request, workflow_ack_result, workflow_agent_options, workflow_agent_request, workflow_agent_result, workflow_agent_summary, workflow_cancel_request, workflow_current_phase, workflow_declared_limits, workflow_durable_operation, workflow_execute_request, workflow_execute_result, workflow_get_run_progress_request, workflow_get_run_request, workflow_journal_get_request, workflow_journal_get_result, workflow_journal_put_request, workflow_list_runs_request, workflow_list_runs_result, workflow_log_line, workflow_log_line_kind, workflow_log_request, workflow_pause_checkpoint_action, workflow_pause_checkpoint_request, workflow_pause_checkpoint_result, workflow_pause_info, workflow_pause_request, workflow_phase_observation, workflow_phase_status, workflow_progress_line, workflow_progress_page, workflow_resume_request, workflow_resume_result, workflow_run_consumed, workflow_run_detail, workflow_run_failure, workflow_run_failure_kind, workflow_run_limits, workflow_run_options, workflow_run_request, workflow_run_result, workflow_run_status, workflow_run_summary, workflow_run_terminal, workflow_tool_resume_request, workflow_tool_run_options, workflow_tool_run_request, workspace_diff_contents, workspace_diff_file_change, workspace_diff_file_change_type, workspace_diff_mode, workspace_diff_result, workspaces_add_summary_request, workspaces_add_summary_result, workspaces_autopilot_objective_exists_result, workspaces_checkpoints, workspaces_create_directory_request, workspaces_create_file_request, workspaces_delete_autopilot_objective_result, workspaces_diff_request, workspaces_ensure_request, workspaces_get_workspace_result, workspaces_list_checkpoints_result, workspaces_list_files_result, workspaces_read_autopilot_objective_result, workspaces_read_checkpoint_request, workspaces_read_checkpoint_result, workspaces_read_file_request, workspaces_read_file_result, workspaces_remove_path_request, workspaces_rename_path_request, workspaces_save_large_paste_request, workspaces_save_large_paste_result, workspaces_stat_file_request, workspaces_stat_file_result, workspaces_truncate_summaries_request, workspace_summary_host_type, workspaces_update_metadata_request, workspaces_workspace_details_host_type, workspaces_write_autopilot_objective_request, workspaces_write_autopilot_objective_result, connector_session_account_result, session_auth_info_result, session_context_attribution, session_context_info, subagent_settings, task_progress, workspace_summary) def to_dict(self) -> dict: result: dict = {} @@ -52873,10 +57943,17 @@ def to_dict(self) -> dict: result["AcceptedEnqueueCommandResult"] = to_class(AcceptedEnqueueCommandResult, self.accepted_enqueue_command_result) result["AccountAllUsers"] = to_class(AccountAllUsers, self.account_all_users) result["AccountAuthInfo"] = to_class(AccountAuthInfo, self.account_auth_info) + result["AccountDescribeAuthInfoRequest"] = to_class(_AccountDescribeAuthInfoRequest, self.account_describe_auth_info_request) + result["AccountDescribeAuthInfoResult"] = to_class(_AccountDescribeAuthInfoResult, self.account_describe_auth_info_result) result["AccountGetAllUsersResult"] = from_list(lambda x: to_class(AccountAllUsers, x), self.account_get_all_users_result) result["AccountGetCurrentAuthResult"] = to_class(AccountGetCurrentAuthResult, self.account_get_current_auth_result) + result["AccountGetGistSupportRequest"] = to_class(_AccountGetGistSupportRequest, self.account_get_gist_support_request) + result["AccountGetGistSupportResult"] = to_class(_AccountGetGistSupportResult, self.account_get_gist_support_result) result["AccountGetQuotaRequest"] = to_class(AccountGetQuotaRequest, self.account_get_quota_request) result["AccountGetQuotaResult"] = to_class(AccountGetQuotaResult, self.account_get_quota_result) + result["AccountGetTokenEnvVarResult"] = to_class(_AccountGetTokenEnvVarResult, self.account_get_token_env_var_result) + result["AccountGetWebUrlRequest"] = to_class(_AccountGetWebURLRequest, self.account_get_web_url_request) + result["AccountGetWebUrlResult"] = to_class(_AccountGetWebURLResult, self.account_get_web_url_result) result["AccountKind"] = to_enum(AccountKind, self.account_kind) result["AccountLoginRequest"] = to_class(AccountLoginRequest, self.account_login_request) result["AccountLoginResult"] = to_class(AccountLoginResult, self.account_login_result) @@ -53227,12 +58304,21 @@ def to_dict(self) -> dict: result["ExternalToolTextResultForLlmContentShellExit"] = to_class(ExternalToolTextResultForLlmContentShellExit, self.external_tool_text_result_for_llm_content_shell_exit) result["ExternalToolTextResultForLlmContentTerminal"] = to_class(ExternalToolTextResultForLlmContentTerminal, self.external_tool_text_result_for_llm_content_terminal) result["ExternalToolTextResultForLlmContentText"] = to_class(ExternalToolTextResultForLlmContentText, self.external_tool_text_result_for_llm_content_text) + result["FeatureFlagAvailability"] = to_enum(_FeatureFlagAvailability, self.feature_flag_availability) + result["FeatureFlagMetadata"] = to_class(_FeatureFlagMetadata, self.feature_flag_metadata) + result["FeatureFlagsMetadataResult"] = to_class(_FeatureFlagsMetadataResult, self.feature_flags_metadata_result) + result["FeatureFlagsResolveRequest"] = to_class(_FeatureFlagsResolveRequest, self.feature_flags_resolve_request) + result["FeatureFlagsResolveResult"] = from_dict(from_bool, self.feature_flags_resolve_result) + result["FeatureFlagsResolveStartupGatesRequest"] = to_class(_FeatureFlagsResolveStartupGatesRequest, self.feature_flags_resolve_startup_gates_request) + result["FeatureFlagsResolveStartupGatesResult"] = from_dict(from_bool, self.feature_flags_resolve_startup_gates_result) result["FilterMapping"] = from_union([lambda x: from_dict(lambda x: to_enum(ContentFilterMode, x), x), lambda x: to_enum(ContentFilterMode, x)], self.filter_mapping) result["FleetStartRequest"] = to_class(FleetStartRequest, self.fleet_start_request) result["FleetStartResult"] = to_class(FleetStartResult, self.fleet_start_result) result["FolderTrustAddParams"] = to_class(FolderTrustAddParams, self.folder_trust_add_params) result["FolderTrustCheckParams"] = to_class(FolderTrustCheckParams, self.folder_trust_check_params) result["FolderTrustCheckResult"] = to_class(FolderTrustCheckResult, self.folder_trust_check_result) + result["FolderTrustIsTrustedParams"] = to_class(_FolderTrustIsTrustedParams, self.folder_trust_is_trusted_params) + result["ForceLoginOrgsDecision"] = to_class(_ForceLoginOrgsDecision, self.force_login_orgs_decision) result["GhCliAuthInfo"] = to_class(GhCLIAuthInfo, self.gh_cli_auth_info) result["GitCurrentBranchRemoteResult"] = to_class(_GitCurrentBranchRemoteResult, self.git_current_branch_remote_result) result["GitCwdRequest"] = to_class(_GitCwdRequest, self.git_cwd_request) @@ -53242,7 +58328,7 @@ def to_dict(self) -> dict: result["GitHubOwnersCancelResult"] = to_class(_GitHubOwnersCancelResult, self.git_hub_owners_cancel_result) result["GitHubOwnersListRequest"] = to_class(_GitHubOwnersListRequest, self.git_hub_owners_list_request) result["GitHubOwnersListResult"] = to_class(_GitHubOwnersListResult, self.git_hub_owners_list_result) - result["GitHubOwnersRequestIdResult"] = to_class(GitHubOwnersRequestIDResult, self.git_hub_owners_request_id_result) + result["GitHubOwnersRequestIdResult"] = to_class(_GitHubOwnersRequestIDResult, self.git_hub_owners_request_id_result) result["GitHubRepositoryAtPathRequest"] = to_class(_GitHubRepositoryAtPathRequest, self.git_hub_repository_at_path_request) result["GitHubRepositoryAtPathResult"] = to_class(_GitHubRepositoryAtPathResult, self.git_hub_repository_at_path_result) result["GitHubRepositoryIdentity"] = to_class(GitHubRepositoryIdentity, self.git_hub_repository_identity) @@ -53328,9 +58414,11 @@ def to_dict(self) -> dict: result["InstructionDiscoveryPathKind"] = to_enum(DebugCollectLogsEntryKind, self.instruction_discovery_path_kind) result["InstructionDiscoveryPathList"] = to_class(InstructionDiscoveryPathList, self.instruction_discovery_path_list) result["InstructionDiscoveryPathLocation"] = to_enum(InstructionLocation, self.instruction_discovery_path_location) + result["InstructionsDiscoverRepoSourcesRequest"] = to_class(_InstructionsDiscoverRepoSourcesRequest, self.instructions_discover_repo_sources_request) result["InstructionsDiscoverRequest"] = to_class(InstructionsDiscoverRequest, self.instructions_discover_request) result["InstructionsGetDiscoveryPathsRequest"] = to_class(InstructionsGetDiscoveryPathsRequest, self.instructions_get_discovery_paths_request) result["InstructionsGetSourcesResult"] = to_class(InstructionsGetSourcesResult, self.instructions_get_sources_result) + result["InstructionsInitPromptResult"] = to_class(_InstructionsInitPromptResult, self.instructions_init_prompt_result) result["InstructionSource"] = to_class(InstructionSource, self.instruction_source) result["InstructionSourceLocation"] = to_enum(InstructionLocation, self.instruction_source_location) result["InstructionSourceType"] = to_enum(InstructionSourceType, self.instruction_source_type) @@ -53350,26 +58438,60 @@ def to_dict(self) -> dict: result["LlmInferenceHttpResponseStartResult"] = to_class(LlmInferenceHTTPResponseStartResult, self.llm_inference_http_response_start_result) result["LlmInferenceSetProviderResult"] = to_class(LlmInferenceSetProviderResult, self.llm_inference_set_provider_result) result["LocalSessionMetadataValue"] = to_class(LocalSessionMetadataValue, self.local_session_metadata_value) + result["LocationPermissionsCleanupResult"] = to_class(_LocationPermissionsCleanupResult, self.location_permissions_cleanup_result) result["LoggedInUser"] = to_class(_LoggedInUser, self.logged_in_user) result["LoginProviderKind"] = to_enum(LoginProviderKind, self.login_provider_kind) result["LogRequest"] = to_class(LogRequest, self.log_request) result["LogResult"] = to_class(LogResult, self.log_result) result["LspInitializeRequest"] = to_class(LspInitializeRequest, self.lsp_initialize_request) result["ManagedMcpServerConfig"] = to_class(ManagedMCPServerConfig, self.managed_mcp_server_config) + result["ManagedPermissionEvaluation"] = to_class(ManagedPermissionEvaluation, self.managed_permission_evaluation) + result["ManagedPermissionOperation"] = to_class(ManagedPermissionOperation, self.managed_permission_operation) + result["ManagedPermissionOperationKind"] = to_enum(ManagedPermissionOperationKind, self.managed_permission_operation_kind) + result["ManagedPermissionsEvaluateRequest"] = to_class(ManagedPermissionsEvaluateRequest, self.managed_permissions_evaluate_request) + result["ManagedPermissionsEvaluateResult"] = to_class(ManagedPermissionsEvaluateResult, self.managed_permissions_evaluate_result) + result["ManagedPermissionVerdict"] = to_enum(ManagedPermissionVerdict, self.managed_permission_verdict) + result["ManagedPluginRetryEntry"] = to_class(ManagedPluginRetryEntry, self.managed_plugin_retry_entry) + result["ManagedPluginRetryStatus"] = to_enum(ManagedPluginRetryStatus, self.managed_plugin_retry_status) result["ManagedSettingMeta"] = to_class(ManagedSettingMeta, self.managed_setting_meta) + result["ManagedSettingsApplyControlsParams"] = to_class(_ManagedSettingsApplyControlsParams, self.managed_settings_apply_controls_params) + result["ManagedSettingsApplyControlsResult"] = to_class(_ManagedSettingsApplyControlsResult, self.managed_settings_apply_controls_result) + result["ManagedSettingsBypassDisableSource"] = to_enum(_ManagedSettingsBypassDisableSource, self.managed_settings_bypass_disable_source) + result["ManagedSettingsBypassPolicyResolution"] = to_enum(_ManagedSettingsBypassPolicyResolution, self.managed_settings_bypass_policy_resolution) result["ManagedSettingsChannel"] = to_enum(ManagedSettingsChannel, self.managed_settings_channel) result["ManagedSettingsComposeLayer"] = to_class(ManagedSettingsComposeLayer, self.managed_settings_compose_layer) result["ManagedSettingsComposeRequest"] = to_class(ManagedSettingsComposeRequest, self.managed_settings_compose_request) result["ManagedSettingsComposeResult"] = to_class(ManagedSettingsComposeResult, self.managed_settings_compose_result) result["ManagedSettingsDiagnostic"] = to_class(ManagedSettingsDiagnostic, self.managed_settings_diagnostic) result["ManagedSettingsDiagnosticSeverity"] = to_enum(ManagedSettingsDiagnosticSeverity, self.managed_settings_diagnostic_severity) + result["ManagedSettingsIngestParams"] = to_class(_ManagedSettingsIngestParams, self.managed_settings_ingest_params) + result["ManagedSettingsIngestResult"] = to_class(_ManagedSettingsIngestResult, self.managed_settings_ingest_result) result["ManagedSettingsLayer"] = to_class(ManagedSettingsLayer, self.managed_settings_layer) + result["ManagedSettingsLoadRemoteControlParams"] = to_class(_ManagedSettingsLoadRemoteControlParams, self.managed_settings_load_remote_control_params) + result["ManagedSettingsLoadRemoteControlResult"] = to_class(_ManagedSettingsLoadRemoteControlResult, self.managed_settings_load_remote_control_result) + result["ManagedSettingsLogLevel"] = to_enum(_ManagedSettingsLogLevel, self.managed_settings_log_level) + result["ManagedSettingsLogMessage"] = to_class(_ManagedSettingsLogMessage, self.managed_settings_log_message) result["ManagedSettingsMeta"] = to_class(ManagedSettingsMeta, self.managed_settings_meta) + result["ManagedSettingsParseClientValueParams"] = to_class(_ManagedSettingsParseClientValueParams, self.managed_settings_parse_client_value_params) + result["ManagedSettingsParseClientValueResult"] = to_class(_ManagedSettingsParseClientValueResult, self.managed_settings_parse_client_value_result) + result["ManagedSettingsPolicyHelperAuthority"] = to_enum(_ManagedSettingsPolicyHelperAuthority, self.managed_settings_policy_helper_authority) + result["ManagedSettingsPolicyHelperReason"] = to_enum(_ManagedSettingsPolicyHelperReason, self.managed_settings_policy_helper_reason) + result["ManagedSettingsReadDeviceResult"] = to_class(_ManagedSettingsReadDeviceResult, self.managed_settings_read_device_result) result["ManagedSettingsReadResult"] = to_class(ManagedSettingsReadResult, self.managed_settings_read_result) + result["ManagedSettingsResolveBypassDisableTransitionParams"] = to_class(_ManagedSettingsResolveBypassDisableTransitionParams, self.managed_settings_resolve_bypass_disable_transition_params) + result["ManagedSettingsResolveBypassDisableTransitionResult"] = to_class(_ManagedSettingsResolveBypassDisableTransitionResult, self.managed_settings_resolve_bypass_disable_transition_result) result["ManagedSettingsResolvedData"] = to_class(ManagedSettingsResolvedData, self.managed_settings_resolved_data) + result["ManagedSettingsResolveDeviceForceLoginOrgsGateParams"] = to_class(_ManagedSettingsResolveDeviceForceLoginOrgsGateParams, self.managed_settings_resolve_device_force_login_orgs_gate_params) + result["ManagedSettingsResolveEffectiveValueParams"] = to_class(_ManagedSettingsResolveEffectiveValueParams, self.managed_settings_resolve_effective_value_params) + result["ManagedSettingsResolveEffectiveValueResult"] = to_class(_ManagedSettingsResolveEffectiveValueResult, self.managed_settings_resolve_effective_value_result) result["ManagedSettingsResolveRequest"] = to_class(ManagedSettingsResolveRequest, self.managed_settings_resolve_request) result["ManagedSettingsResolveResult"] = to_class(ManagedSettingsResolveResult, self.managed_settings_resolve_result) + result["ManagedSettingsResolveStartupPolicyParams"] = to_class(_ManagedSettingsResolveStartupPolicyParams, self.managed_settings_resolve_startup_policy_params) + result["ManagedSettingsResolveStartupPolicyResult"] = to_class(_ManagedSettingsResolveStartupPolicyResult, self.managed_settings_resolve_startup_policy_result) + result["ManagedSettingsRetainPolicyParams"] = to_class(_ManagedSettingsRetainPolicyParams, self.managed_settings_retain_policy_params) result["ManagedSettingsSchemaResult"] = to_class(ManagedSettingsSchemaResult, self.managed_settings_schema_result) + result["ManagedSettingsServerPolicyCacheMode"] = to_enum(_ManagedSettingsServerPolicyCacheMode, self.managed_settings_server_policy_cache_mode) + result["ManagedSettingsServerPolicyResolution"] = to_enum(_ManagedSettingsServerPolicyResolution, self.managed_settings_server_policy_resolution) result["ManagedSettingsValidateRequest"] = to_class(ManagedSettingsValidateRequest, self.managed_settings_validate_request) result["ManagedSettingsValidateResult"] = to_class(ManagedSettingsValidateResult, self.managed_settings_validate_result) result["ManagedSettingsValues"] = to_class(ManagedSettingsValues, self.managed_settings_values) @@ -53417,7 +58539,7 @@ def to_dict(self) -> dict: result["McpConfiguredServer"] = to_class(MCPConfiguredServer, self.mcp_configured_server) result["McpConfiguredServerList"] = to_class(MCPConfiguredServerList, self.mcp_configured_server_list) result["McpConfiguredServerState"] = to_class(MCPConfiguredServerState, self.mcp_configured_server_state) - result["McpConfigureGitHubRequest"] = to_class(MCPConfigureGitHubRequest, self.mcp_configure_git_hub_request) + result["McpConfigureGitHubRequest"] = to_class(_MCPConfigureGitHubRequest, self.mcp_configure_git_hub_request) result["McpConfigureGitHubResult"] = to_class(MCPConfigureGitHubResult, self.mcp_configure_git_hub_result) result["McpDiagnosticDetails"] = to_class(MCPDiagnosticDetails, self.mcp_diagnostic_details) result["McpDiagnosticDirection"] = to_enum(MCPDiagnosticDirection, self.mcp_diagnostic_direction) @@ -53455,6 +58577,7 @@ def to_dict(self) -> dict: result["McpInstallPlan"] = to_class(MCPInstallPlan, self.mcp_install_plan) result["McpIsServerRunningRequest"] = to_class(MCPIsServerRunningRequest, self.mcp_is_server_running_request) result["McpIsServerRunningResult"] = to_class(MCPIsServerRunningResult, self.mcp_is_server_running_result) + result["McpListBuiltinServerNamesResult"] = to_class(_MCPListBuiltinServerNamesResult, self.mcp_list_builtin_server_names_result) result["McpListToolsRequest"] = to_class(MCPListToolsRequest, self.mcp_list_tools_request) result["McpListToolsResult"] = to_class(MCPListToolsResult, self.mcp_list_tools_result) result["McpOauthAuthenticationStateChangedRequest"] = to_class(MCPOauthAuthenticationStateChangedRequest, self.mcp_oauth_authentication_state_changed_request) @@ -53521,9 +58644,14 @@ def to_dict(self) -> dict: result["McpPromptsGetResult"] = to_class(MCPPromptsGetResult, self.mcp_prompts_get_result) result["McpPromptsListRequest"] = to_class(MCPPromptsListRequest, self.mcp_prompts_list_request) result["McpPromptsListResult"] = to_class(MCPPromptsListResult, self.mcp_prompts_list_result) - result["McpRegisterExternalClientRequest"] = to_class(MCPRegisterExternalClientRequest, self.mcp_register_external_client_request) - result["McpReloadConfig"] = to_class(MCPReloadConfig, self.mcp_reload_config) - result["McpReloadWithConfigRequest"] = to_class(MCPReloadWithConfigRequest, self.mcp_reload_with_config_request) + result["McpRegisterExternalClientRequest"] = to_class(_MCPRegisterExternalClientRequest, self.mcp_register_external_client_request) + result["McpRegistryCancelRequest"] = to_class(_MCPRegistryCancelRequest, self.mcp_registry_cancel_request) + result["McpRegistryCancelResult"] = to_class(_MCPRegistryCancelResult, self.mcp_registry_cancel_result) + result["McpRegistryRequestIdResult"] = to_class(_MCPRegistryRequestIDResult, self.mcp_registry_request_id_result) + result["McpRegistrySearchRequest"] = to_class(_MCPRegistrySearchRequest, self.mcp_registry_search_request) + result["McpRegistrySearchResult"] = to_class(_MCPRegistrySearchResult, self.mcp_registry_search_result) + result["McpReloadConfig"] = to_class(_MCPReloadConfig, self.mcp_reload_config) + result["McpReloadWithConfigRequest"] = to_class(_MCPReloadWithConfigRequest, self.mcp_reload_with_config_request) result["McpRemoveGitHubResult"] = to_class(MCPRemoveGitHubResult, self.mcp_remove_git_hub_result) result["McpResource"] = to_class(MCPResource, self.mcp_resource) result["McpResourceAnnotations"] = to_class(MCPResourceAnnotations, self.mcp_resource_annotations) @@ -53556,8 +58684,8 @@ def to_dict(self) -> dict: result["McpServerConfigHttp"] = to_class(MCPServerConfigHTTP, self.mcp_server_config_http) result["McpServerConfigHttpOauthGrantType"] = to_enum(MCPGrantType, self.mcp_server_config_http_oauth_grant_type) result["McpServerConfigHttpType"] = to_enum(MCPServerConfigHTTPType, self.mcp_server_config_http_type) - result["McpServerConfigMemory"] = to_class(MCPServerConfigMemory, self.mcp_server_config_memory) - result["McpServerConfigMemoryType"] = to_enum(MCPServerConfigMemoryType, self.mcp_server_config_memory_type) + result["McpServerConfigMemory"] = to_class(_MCPServerConfigMemory, self.mcp_server_config_memory) + result["McpServerConfigMemoryType"] = to_enum(_MCPServerConfigMemoryType, self.mcp_server_config_memory_type) result["McpServerConfigStdio"] = to_class(MCPServerConfigStdio, self.mcp_server_config_stdio) result["McpServerConfigStdioType"] = to_enum(MCPServerConfigStdioType, self.mcp_server_config_stdio_type) result["McpServerFailureInfo"] = to_class(MCPServerFailureInfo, self.mcp_server_failure_info) @@ -53567,6 +58695,8 @@ def to_dict(self) -> dict: result["McpSetEnvValueModeDetails"] = to_enum(MCPSetEnvValueModeDetails, self.mcp_set_env_value_mode_details) result["McpSetEnvValueModeParams"] = to_class(MCPSetEnvValueModeParams, self.mcp_set_env_value_mode_params) result["McpSetEnvValueModeResult"] = to_class(MCPSetEnvValueModeResult, self.mcp_set_env_value_mode_result) + result["McpShouldExcludeGitHubToolsRequest"] = to_class(_MCPShouldExcludeGitHubToolsRequest, self.mcp_should_exclude_git_hub_tools_request) + result["McpShouldExcludeGitHubToolsResult"] = to_class(_MCPShouldExcludeGitHubToolsResult, self.mcp_should_exclude_git_hub_tools_result) result["McpSourceFile"] = to_class(MCPSourceFile, self.mcp_source_file) result["McpSourcePlugin"] = to_class(MCPSourcePlugin, self.mcp_source_plugin) result["McpSourceRef"] = to_class(MCPSourceRef, self.mcp_source_ref) @@ -53578,7 +58708,7 @@ def to_dict(self) -> dict: result["McpToolUi"] = to_class(MCPToolUI, self.mcp_tool_ui) result["McpToolUiVisibility"] = to_enum(MCPToolUIVisibility, self.mcp_tool_ui_visibility) result["McpUninstallPlan"] = to_class(MCPUninstallPlan, self.mcp_uninstall_plan) - result["McpUnregisterExternalClientRequest"] = to_class(MCPUnregisterExternalClientRequest, self.mcp_unregister_external_client_request) + result["McpUnregisterExternalClientRequest"] = to_class(_MCPUnregisterExternalClientRequest, self.mcp_unregister_external_client_request) result["MemoryConfiguration"] = to_class(MemoryConfiguration, self.memory_configuration) result["MetadataContextAttributionResult"] = to_class(MetadataContextAttributionResult, self.metadata_context_attribution_result) result["MetadataContextHeaviestMessagesRequest"] = to_class(MetadataContextHeaviestMessagesRequest, self.metadata_context_heaviest_messages_request) @@ -53614,6 +58744,8 @@ def to_dict(self) -> dict: result["ModelCapabilitiesSupports"] = to_class(ModelCapabilitiesSupports, self.model_capabilities_supports) result["ModelClearStartupSeedRequest"] = to_class(_ModelClearStartupSeedRequest, self.model_clear_startup_seed_request) result["ModelClearStartupSeedResult"] = to_class(_ModelClearStartupSeedResult, self.model_clear_startup_seed_result) + result["ModelGetReasoningSummariesRequest"] = to_class(ModelGetReasoningSummariesRequest, self.model_get_reasoning_summaries_request) + result["ModelGetReasoningSummariesResult"] = to_class(ModelGetReasoningSummariesResult, self.model_get_reasoning_summaries_result) result["ModelList"] = to_class(ModelList, self.model_list) result["ModelListRequest"] = self.model_list_request result["ModelMessage"] = to_class(ModelMessage, self.model_message) @@ -53749,6 +58881,8 @@ def to_dict(self) -> dict: result["PermissionsConfigureAdditionalContentExclusionPolicyScope"] = to_enum(AdditionalContentExclusionPolicyScope, self.permissions_configure_additional_content_exclusion_policy_scope) result["PermissionsConfigureParams"] = to_class(PermissionsConfigureParams, self.permissions_configure_params) result["PermissionsConfigureResult"] = to_class(PermissionsConfigureResult, self.permissions_configure_result) + result["PermissionsEvaluateManagedPolicyParams"] = to_class(_PermissionsEvaluateManagedPolicyParams, self.permissions_evaluate_managed_policy_params) + result["PermissionsEvaluateManagedPolicyResult"] = to_class(PermissionsEvaluateManagedPolicyResult, self.permissions_evaluate_managed_policy_result) result["PermissionsFolderTrustAddTrustedResult"] = to_class(PermissionsFolderTrustAddTrustedResult, self.permissions_folder_trust_add_trusted_result) result["PermissionsGetModeRequest"] = to_class(PermissionsGetModeRequest, self.permissions_get_mode_request) result["PermissionsGetModeResult"] = to_class(PermissionsGetModeResult, self.permissions_get_mode_result) @@ -53785,6 +58919,8 @@ def to_dict(self) -> dict: result["PermissionsUrlsSetUnrestrictedModeResult"] = to_class(PermissionsUrlsSetUnrestrictedModeResult, self.permissions_urls_set_unrestricted_mode_result) result["PermissionUrlsConfig"] = to_class(PermissionUrlsConfig, self.permission_urls_config) result["PermissionUrlsSetUnrestrictedModeParams"] = to_class(PermissionUrlsSetUnrestrictedModeParams, self.permission_urls_set_unrestricted_mode_params) + result["PersistenceWriteRawJsonLockedRequest"] = to_class(PersistenceWriteRawJSONLockedRequest, self.persistence_write_raw_json_locked_request) + result["PersistenceWriteRawJsonLockedResult"] = to_class(PersistenceWriteRawJSONLockedResult, self.persistence_write_raw_json_locked_result) result["PingRequest"] = to_class(PingRequest, self.ping_request) result["PingResult"] = to_class(PingResult, self.ping_result) result["PlanReadResult"] = to_class(PlanReadResult, self.plan_read_result) @@ -53929,7 +59065,17 @@ def to_dict(self) -> dict: result["RemoteSessionMetadataValue"] = to_class(RemoteSessionMetadataValue, self.remote_session_metadata_value) result["RemoteSessionMode"] = to_enum(RemoteSessionMode, self.remote_session_mode) result["RemoteSessionRepository"] = to_class(RemoteSessionRepository, self.remote_session_repository) + result["RepoSettingsEditRequest"] = to_class(_RepoSettingsEditRequest, self.repo_settings_edit_request) + result["RepoSettingsEditScope"] = to_enum(_RepoSettingsEditScope, self.repo_settings_edit_scope) + result["RepoSettingsReadRawRequest"] = to_class(_RepoSettingsReadRawRequest, self.repo_settings_read_raw_request) + result["RepoSettingsReadRawResult"] = to_class(_RepoSettingsReadRawResult, self.repo_settings_read_raw_result) + result["RepoSettingsWriteAllRequest"] = to_class(_RepoSettingsWriteAllRequest, self.repo_settings_write_all_request) + result["RepoSettingsWriteKeyRequest"] = to_class(_RepoSettingsWriteKeyRequest, self.repo_settings_write_key_request) result["ResponseFormat"] = to_class(ResponseFormat, self.response_format) + result["SandboxApplyDefaultsRequest"] = to_class(SandboxApplyDefaultsRequest, self.sandbox_apply_defaults_request) + result["SandboxCheckEnforcementCommandResult"] = from_bool(self.sandbox_check_enforcement_command_result) + result["SandboxCheckPlatformSupportResult"] = from_bool(self.sandbox_check_platform_support_result) + result["SandboxCheckWindowsHostLoopbackSupportResult"] = from_bool(self.sandbox_check_windows_host_loopback_support_result) result["SandboxConfig"] = to_class(SandboxConfig, self.sandbox_config) result["SandboxConfigAuth"] = to_class(SandboxConfigAuth, self.sandbox_config_auth) result["SandboxConfigSource"] = to_enum(SandboxConfigSource, self.sandbox_config_source) @@ -53941,20 +59087,44 @@ def to_dict(self) -> dict: result["SandboxConfigUserPolicyNetworkProxy"] = to_class(SandboxConfigUserPolicyNetworkProxy, self.sandbox_config_user_policy_network_proxy) result["SandboxConfigUserPolicySeatbelt"] = to_class(SandboxConfigUserPolicySeatbelt, self.sandbox_config_user_policy_seatbelt) result["SandboxCredentialsConfig"] = to_class(SandboxCredentialsConfig, self.sandbox_credentials_config) + result["SandboxCredentialSuggestion"] = to_class(SandboxCredentialSuggestion, self.sandbox_credential_suggestion) + result["SandboxCredentialSuggestionsResult"] = to_class(SandboxCredentialSuggestionsResult, self.sandbox_credential_suggestions_result) result["SandboxDisableForSessionRequest"] = to_class(SandboxDisableForSessionRequest, self.sandbox_disable_for_session_request) result["SandboxDisableForSessionResult"] = to_class(SandboxDisableForSessionResult, self.sandbox_disable_for_session_result) + result["SandboxDocsUrlResult"] = to_class(SandboxDocsURLResult, self.sandbox_docs_url_result) + result["SandboxEffectiveForOptionalParams"] = to_class(_SandboxEffectiveForOptionalParams, self.sandbox_effective_for_optional_params) + result["SandboxEffectiveForOptionalResult"] = to_class(_SandboxEffectiveForOptionalResult, self.sandbox_effective_for_optional_result) + result["SandboxEnforcementCommandAllowedParams"] = to_class(_SandboxEnforcementCommandAllowedParams, self.sandbox_enforcement_command_allowed_params) result["SandboxEnforcementStatus"] = to_class(SandboxEnforcementStatus, self.sandbox_enforcement_status) + result["SandboxFilterPickerCandidatesParams"] = to_class(_SandboxFilterPickerCandidatesParams, self.sandbox_filter_picker_candidates_params) + result["SandboxFilterPickerCandidatesResult"] = to_class(_SandboxFilterPickerCandidatesResult, self.sandbox_filter_picker_candidates_result) + result["SandboxGetUnsupportedMessageResult"] = from_str(self.sandbox_get_unsupported_message_result) + result["SandboxGetUnsupportedSettingDisabledNoticeResult"] = from_str(self.sandbox_get_unsupported_setting_disabled_notice_result) result["SandboxGrantPathForRequestRequest"] = to_class(SandboxGrantPathForRequestRequest, self.sandbox_grant_path_for_request_request) result["SandboxGrantPathForRequestResult"] = to_class(SandboxGrantPathForRequestResult, self.sandbox_grant_path_for_request_result) result["SandboxHostCapability"] = to_class(SandboxHostCapability, self.sandbox_host_capability) result["SandboxHostCapabilityName"] = from_str(self.sandbox_host_capability_name) result["SandboxHostSupport"] = to_class(SandboxHostSupport, self.sandbox_host_support) result["SandboxMaskedEnvVar"] = to_class(SandboxMaskedEnvVar, self.sandbox_masked_env_var) + result["SandboxPickerCandidate"] = to_class(_SandboxPickerCandidate, self.sandbox_picker_candidate) + result["SandboxPreparePickerPolicyParams"] = to_class(_SandboxPreparePickerPolicyParams, self.sandbox_prepare_picker_policy_params) + result["SandboxPreparePickerPolicyResult"] = to_class(_SandboxPreparePickerPolicyResult, self.sandbox_prepare_picker_policy_result) + result["SandboxProxyCaCheckInstallationSupportResult"] = from_bool(self.sandbox_proxy_ca_check_installation_support_result) + result["SandboxProxyCaCheckRelevanceResult"] = from_bool(self.sandbox_proxy_ca_check_relevance_result) result["SandboxProxyCaCreateResult"] = to_class(SandboxProxyCACreateResult, self.sandbox_proxy_ca_create_result) result["SandboxProxyCaRequest"] = to_class(SandboxProxyCARequest, self.sandbox_proxy_ca_request) result["SandboxProxyCaState"] = to_enum(SandboxProxyCAState, self.sandbox_proxy_ca_state) result["SandboxProxyCaStatus"] = to_class(SandboxProxyCAStatus, self.sandbox_proxy_ca_status) + result["SandboxProxySecretGetResult"] = to_class(SandboxProxySecretGetResult, self.sandbox_proxy_secret_get_result) + result["SandboxProxySecretLocation"] = to_class(SandboxProxySecretLocation, self.sandbox_proxy_secret_location) + result["SandboxProxySecretSaveRequest"] = to_class(SandboxProxySecretSaveRequest, self.sandbox_proxy_secret_save_request) + result["SandboxProxySecretSaveResult"] = to_class(SandboxProxySecretSaveResult, self.sandbox_proxy_secret_save_result) + result["SandboxResolveFlooredConfigParams"] = to_class(_SandboxResolveFlooredConfigParams, self.sandbox_resolve_floored_config_params) + result["SandboxResolveFlooredConfigResult"] = to_class(SandboxResolveFlooredConfigResult, self.sandbox_resolve_floored_config_result) result["SandboxSessionChange"] = to_enum(SandboxSessionChange, self.sandbox_session_change) + result["SandboxSettingsApplyDefaultsResult"] = from_dict(lambda x: x, self.sandbox_settings_apply_defaults_result) + result["SandboxUnsupportedStartupNoticeParams"] = to_class(_SandboxUnsupportedStartupNoticeParams, self.sandbox_unsupported_startup_notice_params) + result["SandboxUnsupportedStartupNoticeResult"] = to_class(_SandboxUnsupportedStartupNoticeResult, self.sandbox_unsupported_startup_notice_result) result["ScheduleAddAtRequest"] = to_class(ScheduleAddAtRequest, self.schedule_add_at_request) result["ScheduleAddCronRequest"] = to_class(ScheduleAddCronRequest, self.schedule_add_cron_request) result["ScheduleAddRequest"] = to_class(ScheduleAddRequest, self.schedule_add_request) @@ -54085,6 +59255,8 @@ def to_dict(self) -> dict: result["SessionPluginsInstallRequest"] = to_class(SessionPluginsInstallRequest, self.session_plugins_install_request) result["SessionPluginsMarketplacesRefreshRequest"] = to_class(SessionPluginsMarketplacesRefreshRequest, self.session_plugins_marketplaces_refresh_request) result["SessionPluginsReloadRequest"] = to_class(SessionPluginsReloadRequest, self.session_plugins_reload_request) + result["SessionPluginsRetryManagedRequest"] = to_class(SessionPluginsRetryManagedRequest, self.session_plugins_retry_managed_request) + result["SessionPluginsRetryManagedResult"] = to_class(SessionPluginsRetryManagedResult, self.session_plugins_retry_managed_result) result["SessionProviderGetEndpointRequest"] = to_class(SessionProviderGetEndpointRequest, self.session_provider_get_endpoint_request) result["SessionPruneResult"] = to_class(SessionPruneResult, self.session_prune_result) result["SessionQuotaDelegateWarning"] = to_class(SessionQuotaDelegateWarning, self.session_quota_delegate_warning) @@ -54181,6 +59353,9 @@ def to_dict(self) -> dict: result["SessionWorkingDirectoryContextWithClient"] = to_class(SessionWorkingDirectoryContextWithClient, self.session_working_directory_context_with_client) result["SettableAuthInfo"] = (self.settable_auth_info).to_dict() result["SettableTokenAuthInfo"] = to_class(SettableTokenAuthInfo, self.settable_token_auth_info) + result["SettingsEditOperation"] = to_enum(_SettingsEditOperation, self.settings_edit_operation) + result["SettingsEditResult"] = to_class(_SettingsEditResult, self.settings_edit_result) + result["SettingsEditStatus"] = to_enum(_SettingsEditStatus, self.settings_edit_status) result["ShellCancelUserRequestedRequest"] = to_class(ShellCancelUserRequestedRequest, self.shell_cancel_user_requested_request) result["ShellCredentials"] = to_class(ShellCredentials, self.shell_credentials) result["ShellExecRequest"] = to_class(ShellExecRequest, self.shell_exec_request) @@ -54253,6 +59428,8 @@ def to_dict(self) -> dict: result["SlashCommandShowDialogResult"] = to_class(SlashCommandShowDialogResult, self.slash_command_show_dialog_result) result["SlashCommandTextResult"] = to_class(SlashCommandTextResult, self.slash_command_text_result) result["SlashCommandTimelineEntry"] = to_class(SlashCommandTimelineEntry, self.slash_command_timeline_entry) + result["StaffLogLevelMigrationStatus"] = to_enum(StaffLogLevelMigrationStatus, self.staff_log_level_migration_status) + result["StaffUpdateChannelMigrationStatus"] = to_enum(StaffUpdateChannelMigrationStatus, self.staff_update_channel_migration_status) result["SubagentSettingsEntry"] = to_class(SubagentSettingsEntry, self.subagent_settings_entry) result["SubagentSettingsEntryContextTier"] = to_enum(SubagentSettingsEntryContextTier, self.subagent_settings_entry_context_tier) result["SystemMessageBlock"] = to_class(SystemMessageBlock, self.system_message_block) @@ -54367,8 +59544,55 @@ def to_dict(self) -> dict: result["UserAuthInfo"] = to_class(UserAuthInfo, self.user_auth_info) result["UserRequestedShellCommandResult"] = to_class(UserRequestedShellCommandResult, self.user_requested_shell_command_result) result["UserSettingMetadata"] = to_class(UserSettingMetadata, self.user_setting_metadata) + result["UserSettingsApplyTopKeyRequest"] = to_class(UserSettingsApplyTopKeyRequest, self.user_settings_apply_top_key_request) + result["UserSettingsApplyTopKeyResult"] = to_class(UserSettingsApplyTopKeyResult, self.user_settings_apply_top_key_result) + result["UserSettingsBuildRuntimeRequest"] = to_class(_UserSettingsBuildRuntimeRequest, self.user_settings_build_runtime_request) + result["UserSettingsBuildRuntimeResult"] = from_dict(lambda x: x, self.user_settings_build_runtime_result) + result["UserSettingsEditRequest"] = to_class(_UserSettingsEditRequest, self.user_settings_edit_request) + result["UserSettingsGetPathsRequest"] = to_class(UserSettingsGetPathsRequest, self.user_settings_get_paths_request) + result["UserSettingsGetPathsResult"] = to_class(UserSettingsGetPathsResult, self.user_settings_get_paths_result) result["UserSettingsGetResult"] = to_class(UserSettingsGetResult, self.user_settings_get_result) + result["UserSettingsGovernanceKeysResult"] = to_class(UserSettingsGovernanceKeysResult, self.user_settings_governance_keys_result) + result["UserSettingsHostContext"] = to_class(UserSettingsHostContext, self.user_settings_host_context) + result["UserSettingsLoadAndMergeRepoRequest"] = to_class(_UserSettingsLoadAndMergeRepoRequest, self.user_settings_load_and_merge_repo_request) + result["UserSettingsLoadAndMergeRepoResult"] = to_class(_UserSettingsLoadAndMergeRepoResult, self.user_settings_load_and_merge_repo_result) + result["UserSettingsLoadForConfigDirRequest"] = to_class(_UserSettingsLoadForConfigDirRequest, self.user_settings_load_for_config_dir_request) + result["UserSettingsLoadForConfigDirResult"] = from_dict(lambda x: x, self.user_settings_load_for_config_dir_result) + result["UserSettingsLoadForEditResult"] = to_class(UserSettingsLoadForEditResult, self.user_settings_load_for_edit_result) + result["UserSettingsLoadRepoRequest"] = to_class(UserSettingsLoadRepoRequest, self.user_settings_load_repo_request) + result["UserSettingsLoadRepoResult"] = to_class(UserSettingsLoadRepoResult, self.user_settings_load_repo_result) + result["UserSettingsLoadWithWarningResult"] = to_class(UserSettingsLoadWithWarningResult, self.user_settings_load_with_warning_result) + result["UserSettingsMergeManagedAndUserParams"] = to_class(_UserSettingsMergeManagedAndUserParams, self.user_settings_merge_managed_and_user_params) + result["UserSettingsMergeManagedAndUserResult"] = to_class(UserSettingsMergeManagedAndUserResult, self.user_settings_merge_managed_and_user_result) + result["UserSettingsMergeRepoValueParams"] = to_class(_UserSettingsMergeRepoValueParams, self.user_settings_merge_repo_value_params) + result["UserSettingsMergeRepoValueResult"] = to_class(UserSettingsMergeRepoValueResult, self.user_settings_merge_repo_value_result) + result["UserSettingsMigrateObsoleteOnDiskRequest"] = to_class(_UserSettingsMigrateObsoleteOnDiskRequest, self.user_settings_migrate_obsolete_on_disk_request) + result["UserSettingsMigrateObsoleteOnDiskResult"] = to_class(_UserSettingsMigrateObsoleteOnDiskResult, self.user_settings_migrate_obsolete_on_disk_result) + result["UserSettingsMigrateObsoleteOnDiskStatus"] = to_enum(_UserSettingsMigrateObsoleteOnDiskStatus, self.user_settings_migrate_obsolete_on_disk_status) + result["UserSettingsMigrateStaffLogLevelOnceRequest"] = to_class(_UserSettingsMigrateStaffLogLevelOnceRequest, self.user_settings_migrate_staff_log_level_once_request) + result["UserSettingsMigrateStaffLogLevelOnceResult"] = to_class(_UserSettingsMigrateStaffLogLevelOnceResult, self.user_settings_migrate_staff_log_level_once_result) + result["UserSettingsMigrateStaffUpdateChannelOnceResult"] = to_class(_UserSettingsMigrateStaffUpdateChannelOnceResult, self.user_settings_migrate_staff_update_channel_once_result) + result["UserSettingsNormalizeRequest"] = to_class(UserSettingsNormalizeRequest, self.user_settings_normalize_request) + result["UserSettingsNormalizeResult"] = to_class(UserSettingsNormalizeResult, self.user_settings_normalize_result) + result["UserSettingsPathResult"] = to_class(UserSettingsPathResult, self.user_settings_path_result) + result["UserSettingsProblem"] = to_class(UserSettingsProblem, self.user_settings_problem) + result["UserSettingsProblemLocation"] = to_class(UserSettingsProblemLocation, self.user_settings_problem_location) + result["UserSettingsProblemSource"] = to_class(UserSettingsProblemSource, self.user_settings_problem_source) + result["UserSettingsProblemsRequest"] = to_class(UserSettingsProblemsRequest, self.user_settings_problems_request) + result["UserSettingsProblemsResult"] = to_class(UserSettingsProblemsResult, self.user_settings_problems_result) + result["UserSettingsResetStaffModelSelectionOnceRequest"] = to_class(_UserSettingsResetStaffModelSelectionOnceRequest, self.user_settings_reset_staff_model_selection_once_request) + result["UserSettingsResetStaffModelSelectionOnceResult"] = to_class(_UserSettingsResetStaffModelSelectionOnceResult, self.user_settings_reset_staff_model_selection_once_result) + result["UserSettingsSetHookDisabledRequest"] = to_class(_UserSettingsSetHookDisabledRequest, self.user_settings_set_hook_disabled_request) + result["UserSettingsSetHookDisabledResult"] = to_class(_UserSettingsSetHookDisabledResult, self.user_settings_set_hook_disabled_result) result["UserSettingsSetRequest"] = to_class(UserSettingsSetRequest, self.user_settings_set_request) + result["UserSettingsUnknownKeysWarningRequest"] = to_class(_UserSettingsUnknownKeysWarningRequest, self.user_settings_unknown_keys_warning_request) + result["UserSettingsUnknownKeysWarningResult"] = to_class(_UserSettingsUnknownKeysWarningResult, self.user_settings_unknown_keys_warning_result) + result["UserSettingsUpdateRequest"] = to_class(_UserSettingsUpdateRequest, self.user_settings_update_request) + result["UserSettingsUpdateResult"] = to_class(_UserSettingsUpdateResult, self.user_settings_update_result) + result["UserSettingsValidateRequest"] = to_class(UserSettingsValidateRequest, self.user_settings_validate_request) + result["UserSettingsValidateResult"] = to_class(UserSettingsValidateResult, self.user_settings_validate_result) + result["UserSettingsValidationIssue"] = to_class(UserSettingsValidationIssue, self.user_settings_validation_issue) + result["UserSettingsWriteAllRequest"] = to_class(UserSettingsWriteAllRequest, self.user_settings_write_all_request) result["VisibilityGetResult"] = to_class(VisibilityGetResult, self.visibility_get_result) result["VisibilitySetRequest"] = to_class(VisibilitySetRequest, self.visibility_set_request) result["VisibilitySetResult"] = to_class(VisibilitySetResult, self.visibility_set_result) @@ -54462,21 +59686,10 @@ def to_dict(self) -> dict: result["WorkspaceSummary"] = from_union([lambda x: to_class(WorkspaceSummary, x), from_none], self.workspace_summary) return result -def rpc_from_dict(s: Any) -> RPC: - return RPC.from_dict(s) - -def rpc_to_dict(x: RPC) -> Any: - return to_class(RPC, x) - # Experimental: this type is part of an experimental API and may change or be removed. -class ConnectorDiscoveryAvailability(Enum): - "Availability." - # Enabled. - ENABLED = "enabled" - # Disabled. - DISABLED = "disabled" - # Unavailable. - UNAVAILABLE = "unavailable" +class ManagedPermissionOperationKind(Enum): + "Operation to evaluate. Currently only url is supported." + URL = "url" # Outcome of an agentRegistry.spawn call. AgentRegistrySpawnResult = AgentRegistrySpawnSpawned | AgentRegistrySpawnError | AgentRegistrySpawnRegistryTimeout | AgentRegistrySpawnValidationError @@ -54894,6 +60107,8 @@ def _load_TaskInfo(obj: Any) -> "TaskInfo": EventsCursorStatus = CursorStatus ExternalToolResult = ExternalToolTextResultForLlm ExternalToolTextResultForLlmContentResourceLinkIconTheme = Theme +FeatureFlagsResolveResult = dict +FeatureFlagsResolveStartupGatesResult = dict FilterMapping = dict HistoryCompactRequest = Any InstructionDiscoveryPathKind = DebugCollectLogsEntryKind @@ -54939,7 +60154,15 @@ def _load_TaskInfo(obj: Any) -> "TaskInfo": ProviderGetEndpointRequest = Any QuotaTakeWarningsResult = list RemoteSessionMetadataTaskType = TaskType +SandboxCheckEnforcementCommandResult = bool +SandboxCheckPlatformSupportResult = bool +SandboxCheckWindowsHostLoopbackSupportResult = bool +SandboxGetUnsupportedMessageResult = str +SandboxGetUnsupportedSettingDisabledNoticeResult = str SandboxHostCapabilityName = str +SandboxProxyCaCheckInstallationSupportResult = bool +SandboxProxyCaCheckRelevanceResult = bool +SandboxSettingsApplyDefaultsResult = dict SessionAuthInfoResult = AuthIdentity | None SessionCancelAllBackgroundAgentsResult = int SessionContextHostType = HostType @@ -54959,6 +60182,8 @@ def _load_TaskInfo(obj: Any) -> "TaskInfo": TaskInfoExecutionMode = TaskExecutionMode TaskInfoStatus = TaskStatus ToolResult = ToolResultExpanded +UserSettingsBuildRuntimeResult = dict +UserSettingsLoadForConfigDirResult = dict WorkspaceSummaryHostType = HostType WorkspacesWorkspaceDetailsHostType = HostType @@ -54990,6 +60215,123 @@ def _patch_model_capabilities(data: dict) -> dict: return data +# Experimental: this API group is experimental and may change or be removed. +class ServerSandboxProxyCaApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + + async def check_installation_support(self, *, timeout: float | None = None) -> bool: + "Reports whether this runtime process can offer installing the sandbox credential-proxy certificate authority into OS trust.\n\nReturns:\n True when this process can offer installing the sandbox credential-proxy certificate authority into OS trust." + return bool(await self._client.request("sandbox.proxyCa.checkInstallationSupport", {}, **_timeout_kwargs(timeout))) + + async def check_relevance(self, params: SandboxProxyCARequest, *, timeout: float | None = None) -> bool: + "Reports whether this sandbox configuration uses the credential proxy and can need certificate-authority setup.\n\nArgs:\n params: Identifies the credential hosts that the persistent certificate authority of the sandbox credential proxy must cover. The runtime always adds the hosts from the saved user settings.\n\nReturns:\n True when the sandbox configuration uses the credential proxy and can need certificate-authority setup." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return bool(await self._client.request("sandbox.proxyCa.checkRelevance", params_dict, **_timeout_kwargs(timeout))) + + async def get_status(self, params: SandboxProxyCARequest, *, timeout: float | None = None) -> SandboxProxyCAStatus: + "Reports whether the persistent certificate authority of the sandbox credential proxy exists, whether OS trust includes it, and whether it must be rotated. Changes nothing.\n\nArgs:\n params: Identifies the credential hosts that the persistent certificate authority of the sandbox credential proxy must cover. The runtime always adds the hosts from the saved user settings.\n\nReturns:\n Status of the persistent certificate authority of the sandbox credential proxy." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return SandboxProxyCAStatus.from_dict(await self._client.request("sandbox.proxyCa.getStatus", params_dict, **_timeout_kwargs(timeout))) + + async def create(self, params: SandboxProxyCARequest, *, timeout: float | None = None) -> SandboxProxyCACreateResult: + "Creates the persistent certificate authority of the sandbox credential proxy if none is stored, without changing OS trust, and returns the path of its public certificate. Keeps an existing certificate authority, even one that must be rotated. Fails where OS trust is unsupported. Trust it with sandbox.proxyCa.trust: the CLI trusts only the hosts in the saved user settings, so it refuses a certificate authority that also covers hosts from sandboxConfig.\n\nArgs:\n params: Identifies the credential hosts that the persistent certificate authority of the sandbox credential proxy must cover. The runtime always adds the hosts from the saved user settings.\n\nReturns:\n Result of creating the persistent certificate authority of the sandbox credential proxy." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return SandboxProxyCACreateResult.from_dict(await self._client.request("sandbox.proxyCa.create", params_dict, **_timeout_kwargs(timeout))) + + async def rotate(self, params: SandboxProxyCARequest, *, timeout: float | None = None) -> SandboxProxyCAStatus: + "Replaces the persistent certificate authority of the sandbox credential proxy with a new one for the current credential hosts. If OS trust included the old one, removes it and trusts the new one, which can show an OS authentication prompt. Running sandboxed tools keep the old certificate authority until they restart.\n\nArgs:\n params: Identifies the credential hosts that the persistent certificate authority of the sandbox credential proxy must cover. The runtime always adds the hosts from the saved user settings.\n\nReturns:\n Status of the persistent certificate authority of the sandbox credential proxy." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return SandboxProxyCAStatus.from_dict(await self._client.request("sandbox.proxyCa.rotate", params_dict, **_timeout_kwargs(timeout))) + + async def trust(self, params: SandboxProxyCARequest, *, timeout: float | None = None) -> SandboxProxyCAStatus: + "Adds the persistent certificate authority of the sandbox credential proxy to OS trust, so sandboxed clients that read only OS trust accept the proxy. Call create first. Refuses a certificate authority that is not constrained to the current credential hosts. Can show an OS authentication prompt.\n\nArgs:\n params: Identifies the credential hosts that the persistent certificate authority of the sandbox credential proxy must cover. The runtime always adds the hosts from the saved user settings.\n\nReturns:\n Status of the persistent certificate authority of the sandbox credential proxy." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return SandboxProxyCAStatus.from_dict(await self._client.request("sandbox.proxyCa.trust", params_dict, **_timeout_kwargs(timeout))) + + async def remove(self, *, timeout: float | None = None) -> SandboxProxyCAStatus: + "Removes the persistent certificate authority of the sandbox credential proxy from OS trust. Keeps the stored certificate authority. Can show an OS authentication prompt. Sandboxed clients that read only OS trust then reject the proxy; clients that read the per-process certificate bundle continue to work.\n\nReturns:\n Status of the persistent certificate authority of the sandbox credential proxy." + return SandboxProxyCAStatus.from_dict(await self._client.request("sandbox.proxyCa.remove", {}, **_timeout_kwargs(timeout))) + + +# Experimental: this API group is experimental and may change or be removed. +class ServerSandboxApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + self.proxy_ca = ServerSandboxProxyCaApi(client) + + async def get_host_support(self, *, timeout: float | None = None) -> SandboxHostSupport: + "Reports whether the host running this runtime can run the command sandbox, without starting a session or spawning a sandboxed command.\n\nReturns:\n Whether the host running this runtime can run the command sandbox. The runtime checks `supported` once per process. A capability answer can change while the process runs, for example after the user installs a missing package." + return SandboxHostSupport.from_dict(await self._client.request("sandbox.getHostSupport", {}, **_timeout_kwargs(timeout))) + + +# Experimental: this API group is experimental and may change or be removed. +class ServerUserSettingsApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + + async def get(self, *, timeout: float | None = None) -> UserSettingsGetResult: + "Lists every known user setting from settings.json, each with its effective value, its default, and whether it is at the default — so settings the user has never set still appear with their default value. Does not include repository- or enterprise-managed overrides that the runtime layers on top at session time.\n\nReturns:\n Per-key metadata for every known user setting in settings.json, including settings left at their default. Excludes repository- and enterprise-managed overrides." + return UserSettingsGetResult.from_dict(await self._client.request("user.settings.get", {}, **_timeout_kwargs(timeout))) + + async def set(self, params: UserSettingsSetRequest, *, timeout: float | None = None) -> None: + "Writes one or more user settings to settings.json, replacing each provided top-level key. A key whose value is null is removed.\n\nArgs:\n params: Partial user settings to write to settings.json. Each top-level key is written individually, replacing the existing value; a key whose value is null is removed." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + await self._client.request("user.settings.set", params_dict, **_timeout_kwargs(timeout)) + + +# Experimental: this API group is experimental and may change or be removed. +class ServerUserApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + self.settings = ServerUserSettingsApi(client) + + +# Experimental: this API group is experimental and may change or be removed. +class ServerManagedSettingsPermissionsApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + + async def evaluate(self, params: ManagedPermissionsEvaluateRequest, *, timeout: float | None = None) -> ManagedPermissionsEvaluateResult: + "Pure, sessionless evaluation of operations against a supplied permissionsContext. Performs no policy discovery, I/O, prompting, navigation, permission grants, or sandbox changes. Obtain the context from managedSettings.resolve or session.managedSettings.get, or reuse an in-memory context. Results preserve input order and duplicates. Consumers must block deny, obtain approval for ask, and use their normal permission flow for unmanaged. Unknown policy returns failClosed with every operation denied. This is not a substitute for user permissions or other security controls.\n\nArgs:\n params: Managed policy context and ordered operations for pure, sessionless evaluation.\n\nReturns:\n Ordered managed permission verdicts and the supplied policy's fail-closed posture." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return ManagedPermissionsEvaluateResult.from_dict(await self._client.request("managedSettings.permissions.evaluate", params_dict, **_timeout_kwargs(timeout))) + + +# Experimental: this API group is experimental and may change or be removed. +class ServerManagedSettingsApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + self.permissions = ServerManagedSettingsPermissionsApi(client) + + async def read(self, *, timeout: float | None = None) -> ManagedSettingsReadResult: + "Discovers device-managed settings from production MDM and managed-file sources, validates them against the runtime-owned managed-settings schema, and returns the canonical JSON without requiring a session. `managedSettings.resolve` returns the same device settings together with the account's server policy.\n\nReturns:\n Validated device-managed settings discovered before a session exists." + return ManagedSettingsReadResult.from_dict(await self._client.request("managedSettings.read", {}, **_timeout_kwargs(timeout))) + + async def clear_cache(self, *, timeout: float | None = None) -> None: + "Force-refreshes enterprise managed settings for every account: wipes the persistent server-policy cache (the whole `/managed-settings` directory) and drops this runtime process's in-memory retained server policy. It does not itself fetch policy — the effect is that the next time a session resolves managed settings for an account, that resolution re-fetches the account's org policy from the network instead of serving a cached response. Note that `managedSettings.read` returns only device/MDM settings and never triggers the account server-policy fetch, so a host implementing \"sync account policy\" should call `managedSettings.resolve` or start a fresh session resolution rather than treat a subsequent `managedSettings.read` as the refreshed org policy. Mirrors the invalidation a sign-out performs, broadened from the one signing-out account to all of them; device/MDM layers describe the machine, not the account, and are left untouched. Rejects if the on-disk cache cannot be removed." + await self._client.request("managedSettings.clearCache", {}, **_timeout_kwargs(timeout)) + + async def resolve(self, params: ManagedSettingsResolveRequest, *, timeout: float | None = None) -> ManagedSettingsResolveResult: + "Resolves the effective enterprise managed settings without a session, from the device channel and, when an account is available, the account's server policy through the same per-account cache sessions use. A cached server policy less than an hour old is used without a fetch; otherwise the policy is fetched, and when the fetch fails a cached policy up to 24 hours old is used instead, unless `forceRemoteSettingsRefresh` requires a live fetch. With no account requested or signed in, it reports device policy only; signing out removes the account's cached policy. It can fetch server policy over the network when the cache is stale, so call it off latency-critical paths such as startup rather than before listing models. The policy helper is not run. `layers` lists each channel's document before merging, and `values` and `meta` carry typed effective values and their lock state for the keys typed so far.\n\nArgs:\n params: Optional opaque account selection or GitHub token whose managed settings are resolved.\n\nReturns:\n Effective enterprise managed settings for an account, resolved without a session." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return ManagedSettingsResolveResult.from_dict(await self._client.request("managedSettings.resolve", params_dict, **_timeout_kwargs(timeout))) + + async def schema(self, *, timeout: float | None = None) -> ManagedSettingsSchemaResult: + "Returns the managed-settings authoring JSON schema with descriptive `x-composition` annotations aligned with the shared settings-engine vocabulary. These annotations are not a complete runtime composition contract: model, effortLevel, and contextTier remain coupled. Use `managedSettings.compose` for the runtime's effective result. Performs no I/O.\n\nReturns:\n The authoring JSON schema for managed settings recognized by this runtime." + return ManagedSettingsSchemaResult.from_dict(await self._client.request("managedSettings.schema", {}, **_timeout_kwargs(timeout))) + + async def validate(self, params: ManagedSettingsValidateRequest, *, timeout: float | None = None) -> ManagedSettingsValidateResult: + "Validates a candidate managed-settings document the way the runtime validates delivered policy, without applying it. Reports errors that would reject the document, warnings for content the runtime ignores, and the canonical document it would apply. Document text nested more than 64 levels deep is rejected. Performs no I/O.\n\nArgs:\n params: A candidate managed-settings document to validate without applying it.\n\nReturns:\n Result of validating a managed-settings document." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return ManagedSettingsValidateResult.from_dict(await self._client.request("managedSettings.validate", params_dict, **_timeout_kwargs(timeout))) + + async def compose(self, params: ManagedSettingsComposeRequest, *, timeout: float | None = None) -> ManagedSettingsComposeResult: + "Merges candidate managed-settings documents for the device, server, and policy-helper channels into the effective settings the runtime would enforce on this host, using the same precedence and composition rules as live resolution, without applying them. Like live resolution, a server's advisory sandbox force-enable is declined on a host that cannot run the sandbox. Does not fetch policy or read policy files, but may perform blocking OS or subprocess probes for sandbox support. Preview documents are limited to 1 MiB and 64 levels of nesting.\n\nArgs:\n params: Candidate managed-settings documents to merge without applying them.\n\nReturns:\n The effective managed settings the runtime would enforce for the given documents, in the same shape `managedSettings.resolve` returns." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return ManagedSettingsComposeResult.from_dict(await self._client.request("managedSettings.compose", params_dict, **_timeout_kwargs(timeout))) + + # Experimental: this API group is experimental and may change or be removed. class ServerEnvironmentsApi: def __init__(self, client: "JsonRpcClient"): @@ -55063,47 +60405,6 @@ async def get_built_in_catalog(self, *, timeout: float | None = None) -> BuiltIn return BuiltInModelCatalog.from_dict(await self._client.request("models.getBuiltInCatalog", {}, **_timeout_kwargs(timeout))) -# Experimental: this API group is experimental and may change or be removed. -class ServerSandboxProxyCaApi: - def __init__(self, client: "JsonRpcClient"): - self._client = client - - async def get_status(self, params: SandboxProxyCARequest, *, timeout: float | None = None) -> SandboxProxyCAStatus: - "Reports whether the persistent certificate authority of the sandbox credential proxy exists, whether OS trust includes it, and whether it must be rotated. Changes nothing.\n\nArgs:\n params: Identifies the credential hosts that the persistent certificate authority of the sandbox credential proxy must cover. The runtime always adds the hosts from the saved user settings.\n\nReturns:\n Status of the persistent certificate authority of the sandbox credential proxy." - params_dict = {k: v for k, v in params.to_dict().items() if v is not None} - return SandboxProxyCAStatus.from_dict(await self._client.request("sandbox.proxyCa.getStatus", params_dict, **_timeout_kwargs(timeout))) - - async def create(self, params: SandboxProxyCARequest, *, timeout: float | None = None) -> SandboxProxyCACreateResult: - "Creates the persistent certificate authority of the sandbox credential proxy if none is stored, without changing OS trust, and returns the path of its public certificate. Keeps an existing certificate authority, even one that must be rotated. Fails where OS trust is unsupported. Trust it with sandbox.proxyCa.trust: the CLI trusts only the hosts in the saved user settings, so it refuses a certificate authority that also covers hosts from sandboxConfig.\n\nArgs:\n params: Identifies the credential hosts that the persistent certificate authority of the sandbox credential proxy must cover. The runtime always adds the hosts from the saved user settings.\n\nReturns:\n Result of creating the persistent certificate authority of the sandbox credential proxy." - params_dict = {k: v for k, v in params.to_dict().items() if v is not None} - return SandboxProxyCACreateResult.from_dict(await self._client.request("sandbox.proxyCa.create", params_dict, **_timeout_kwargs(timeout))) - - async def rotate(self, params: SandboxProxyCARequest, *, timeout: float | None = None) -> SandboxProxyCAStatus: - "Replaces the persistent certificate authority of the sandbox credential proxy with a new one for the current credential hosts. If OS trust included the old one, removes it and trusts the new one, which can show an OS authentication prompt. Running sandboxed tools keep the old certificate authority until they restart.\n\nArgs:\n params: Identifies the credential hosts that the persistent certificate authority of the sandbox credential proxy must cover. The runtime always adds the hosts from the saved user settings.\n\nReturns:\n Status of the persistent certificate authority of the sandbox credential proxy." - params_dict = {k: v for k, v in params.to_dict().items() if v is not None} - return SandboxProxyCAStatus.from_dict(await self._client.request("sandbox.proxyCa.rotate", params_dict, **_timeout_kwargs(timeout))) - - async def trust(self, params: SandboxProxyCARequest, *, timeout: float | None = None) -> SandboxProxyCAStatus: - "Adds the persistent certificate authority of the sandbox credential proxy to OS trust, so sandboxed clients that read only OS trust accept the proxy. Call create first. Refuses a certificate authority that is not constrained to the current credential hosts. Can show an OS authentication prompt.\n\nArgs:\n params: Identifies the credential hosts that the persistent certificate authority of the sandbox credential proxy must cover. The runtime always adds the hosts from the saved user settings.\n\nReturns:\n Status of the persistent certificate authority of the sandbox credential proxy." - params_dict = {k: v for k, v in params.to_dict().items() if v is not None} - return SandboxProxyCAStatus.from_dict(await self._client.request("sandbox.proxyCa.trust", params_dict, **_timeout_kwargs(timeout))) - - async def remove(self, *, timeout: float | None = None) -> SandboxProxyCAStatus: - "Removes the persistent certificate authority of the sandbox credential proxy from OS trust. Keeps the stored certificate authority. Can show an OS authentication prompt. Sandboxed clients that read only OS trust then reject the proxy; clients that read the per-process certificate bundle continue to work.\n\nReturns:\n Status of the persistent certificate authority of the sandbox credential proxy." - return SandboxProxyCAStatus.from_dict(await self._client.request("sandbox.proxyCa.remove", {}, **_timeout_kwargs(timeout))) - - -# Experimental: this API group is experimental and may change or be removed. -class ServerSandboxApi: - def __init__(self, client: "JsonRpcClient"): - self._client = client - self.proxy_ca = ServerSandboxProxyCaApi(client) - - async def get_host_support(self, *, timeout: float | None = None) -> SandboxHostSupport: - "Reports whether the host running this runtime can run the command sandbox, without starting a session or spawning a sandboxed command.\n\nReturns:\n Whether the host running this runtime can run the command sandbox. The runtime checks `supported` once per process. A capability answer can change while the process runs, for example after the user installs a missing package." - return SandboxHostSupport.from_dict(await self._client.request("sandbox.getHostSupport", {}, **_timeout_kwargs(timeout))) - - # Experimental: this API group is experimental and may change or be removed. class ServerToolsApi: def __init__(self, client: "JsonRpcClient"): @@ -55278,6 +60579,22 @@ async def disable(self, params: DiscoveredExtensionsDisableRequest, *, timeout: await self._client.request("extensions.disable", params_dict, **_timeout_kwargs(timeout)) +# Experimental: this API group is experimental and may change or be removed. +class ServerInstructionsApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + + 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))) + + 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} + return InstructionDiscoveryPathList.from_dict(await self._client.request("instructions.getDiscoveryPaths", params_dict, **_timeout_kwargs(timeout))) + + # Experimental: this API group is experimental and may change or be removed. class ServerSkillsInstallationsApi: def __init__(self, client: "JsonRpcClient"): @@ -55477,22 +60794,6 @@ async def get_discovery_paths(self, params: AgentsGetDiscoveryPathsRequest, *, t return AgentDiscoveryPathList.from_dict(await self._client.request("agents.getDiscoveryPaths", params_dict, **_timeout_kwargs(timeout))) -# Experimental: this API group is experimental and may change or be removed. -class ServerInstructionsApi: - def __init__(self, client: "JsonRpcClient"): - self._client = client - - 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))) - - 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} - return InstructionDiscoveryPathList.from_dict(await self._client.request("instructions.getDiscoveryPaths", params_dict, **_timeout_kwargs(timeout))) - - # Experimental: this API group is experimental and may change or be removed. class ServerCommandsApi: def __init__(self, client: "JsonRpcClient"): @@ -55503,61 +60804,6 @@ async def list(self, *, timeout: float | None = None) -> CommandList: return CommandList.from_dict(await self._client.request("commands.list", {}, **_timeout_kwargs(timeout))) -# Experimental: this API group is experimental and may change or be removed. -class ServerUserSettingsApi: - def __init__(self, client: "JsonRpcClient"): - self._client = client - - async def get(self, *, timeout: float | None = None) -> UserSettingsGetResult: - "Lists every known user setting from settings.json, each with its effective value, its default, and whether it is at the default — so settings the user has never set still appear with their default value. Does not include repository- or enterprise-managed overrides that the runtime layers on top at session time.\n\nReturns:\n Per-key metadata for every known user setting in settings.json, including settings left at their default. Excludes repository- and enterprise-managed overrides." - return UserSettingsGetResult.from_dict(await self._client.request("user.settings.get", {}, **_timeout_kwargs(timeout))) - - async def set(self, params: UserSettingsSetRequest, *, timeout: float | None = None) -> None: - "Writes one or more user settings to settings.json, replacing each provided top-level key. A key whose value is null is removed.\n\nArgs:\n params: Partial user settings to write to settings.json. Each top-level key is written individually, replacing the existing value; a key whose value is null is removed." - params_dict = {k: v for k, v in params.to_dict().items() if v is not None} - await self._client.request("user.settings.set", params_dict, **_timeout_kwargs(timeout)) - - -# Experimental: this API group is experimental and may change or be removed. -class ServerUserApi: - def __init__(self, client: "JsonRpcClient"): - self._client = client - self.settings = ServerUserSettingsApi(client) - - -# Experimental: this API group is experimental and may change or be removed. -class ServerManagedSettingsApi: - def __init__(self, client: "JsonRpcClient"): - self._client = client - - async def read(self, *, timeout: float | None = None) -> ManagedSettingsReadResult: - "Discovers device-managed settings from production MDM and managed-file sources, validates them against the runtime-owned managed-settings schema, and returns the canonical JSON without requiring a session. `managedSettings.resolve` returns the same device settings together with the account's server policy.\n\nReturns:\n Validated device-managed settings discovered before a session exists." - return ManagedSettingsReadResult.from_dict(await self._client.request("managedSettings.read", {}, **_timeout_kwargs(timeout))) - - async def clear_cache(self, *, timeout: float | None = None) -> None: - "Force-refreshes enterprise managed settings for every account: wipes the persistent server-policy cache (the whole `/managed-settings` directory) and drops this runtime process's in-memory retained server policy. It does not itself fetch policy — the effect is that the next time a session resolves managed settings for an account, that resolution re-fetches the account's org policy from the network instead of serving a cached response. Note that `managedSettings.read` returns only device/MDM settings and never triggers the account server-policy fetch, so a host implementing \"sync account policy\" should call `managedSettings.resolve` or start a fresh session resolution rather than treat a subsequent `managedSettings.read` as the refreshed org policy. Mirrors the invalidation a sign-out performs, broadened from the one signing-out account to all of them; device/MDM layers describe the machine, not the account, and are left untouched. Rejects if the on-disk cache cannot be removed." - await self._client.request("managedSettings.clearCache", {}, **_timeout_kwargs(timeout)) - - async def resolve(self, params: ManagedSettingsResolveRequest, *, timeout: float | None = None) -> ManagedSettingsResolveResult: - "Resolves the effective enterprise managed settings without a session, from the device channel and, when an account is available, the account's server policy through the same per-account cache sessions use. A cached server policy less than an hour old is used without a fetch; otherwise the policy is fetched, and when the fetch fails a cached policy up to 24 hours old is used instead, unless `forceRemoteSettingsRefresh` requires a live fetch. With no account requested or signed in, it reports device policy only; signing out removes the account's cached policy. It can fetch server policy over the network when the cache is stale, so call it off latency-critical paths such as startup rather than before listing models. The policy helper is not run. `layers` lists each channel's document before merging, and `values` and `meta` carry typed effective values and their lock state for the keys typed so far.\n\nArgs:\n params: Optional opaque account selection or GitHub token whose managed settings are resolved.\n\nReturns:\n Effective enterprise managed settings for an account, resolved without a session." - params_dict = {k: v for k, v in params.to_dict().items() if v is not None} - return ManagedSettingsResolveResult.from_dict(await self._client.request("managedSettings.resolve", params_dict, **_timeout_kwargs(timeout))) - - async def schema(self, *, timeout: float | None = None) -> ManagedSettingsSchemaResult: - "Returns the managed-settings authoring JSON schema with descriptive `x-composition` annotations aligned with the shared settings-engine vocabulary. These annotations are not a complete runtime composition contract: model, effortLevel, and contextTier remain coupled. Use `managedSettings.compose` for the runtime's effective result. Performs no I/O.\n\nReturns:\n The authoring JSON schema for managed settings recognized by this runtime." - return ManagedSettingsSchemaResult.from_dict(await self._client.request("managedSettings.schema", {}, **_timeout_kwargs(timeout))) - - async def validate(self, params: ManagedSettingsValidateRequest, *, timeout: float | None = None) -> ManagedSettingsValidateResult: - "Validates a candidate managed-settings document the way the runtime validates delivered policy, without applying it. Reports errors that would reject the document, warnings for content the runtime ignores, and the canonical document it would apply. Document text nested more than 64 levels deep is rejected. Performs no I/O.\n\nArgs:\n params: A candidate managed-settings document to validate without applying it.\n\nReturns:\n Result of validating a managed-settings document." - params_dict = {k: v for k, v in params.to_dict().items() if v is not None} - return ManagedSettingsValidateResult.from_dict(await self._client.request("managedSettings.validate", params_dict, **_timeout_kwargs(timeout))) - - async def compose(self, params: ManagedSettingsComposeRequest, *, timeout: float | None = None) -> ManagedSettingsComposeResult: - "Merges candidate managed-settings documents for the device, server, and policy-helper channels into the effective settings the runtime would enforce on this host, using the same precedence and composition rules as live resolution, without applying them. Like live resolution, a server's advisory sandbox force-enable is declined on a host that cannot run the sandbox. Does not fetch policy or read policy files, but may perform blocking OS or subprocess probes for sandbox support. Preview documents are limited to 1 MiB and 64 levels of nesting.\n\nArgs:\n params: Candidate managed-settings documents to merge without applying them.\n\nReturns:\n The effective managed settings the runtime would enforce for the given documents, in the same shape `managedSettings.resolve` returns." - params_dict = {k: v for k, v in params.to_dict().items() if v is not None} - return ManagedSettingsComposeResult.from_dict(await self._client.request("managedSettings.compose", params_dict, **_timeout_kwargs(timeout))) - - # Experimental: this API group is experimental and may change or be removed. class ServerRuntimeApi: def __init__(self, client: "JsonRpcClient"): @@ -55767,24 +61013,24 @@ class ServerRpc: """Typed server-scoped RPC methods.""" def __init__(self, client: "JsonRpcClient"): self._client = client + self.sandbox = ServerSandboxApi(client) + self.user = ServerUserApi(client) + self.managed_settings = ServerManagedSettingsApi(client) self.environments = ServerEnvironmentsApi(client) self.host = ServerHostApi(client) self.hooks = ServerHooksApi(client) self.models = ServerModelsApi(client) - self.sandbox = ServerSandboxApi(client) self.tools = ServerToolsApi(client) self.account = ServerAccountApi(client) self.secrets = ServerSecretsApi(client) self.mcp = ServerMcpApi(client) self.extensions = ServerExtensionsApi(client) + self.instructions = ServerInstructionsApi(client) self.skills = ServerSkillsApi(client) self.catalog = ServerCatalogApi(client) self.plugins = ServerPluginsApi(client) self.agents = ServerAgentsApi(client) - self.instructions = ServerInstructionsApi(client) self.commands = ServerCommandsApi(client) - self.user = ServerUserApi(client) - self.managed_settings = ServerManagedSettingsApi(client) self.runtime = ServerRuntimeApi(client) self.session_fs = ServerSessionFsApi(client) self.llm_inference = ServerLlmInferenceApi(client) @@ -55802,6 +61048,380 @@ async def register_extension_launch_provider(self, *, timeout: float | None = No await self._client.request("registerExtensionLaunchProvider", {}, **_timeout_kwargs(timeout)) +# Experimental: this API group is experimental and may change or be removed. +class _InternalServerSandboxProxySecretApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + + async def _get_location(self, *, timeout: float | None = None) -> SandboxProxySecretLocation: + "Reports where this runtime keeps the sandbox proxy password: the credential store directory and the entry identifier within it. Both are derived from the configuration directory this runtime was started with, so a host does not resolve them itself. Read this to recognize a `${secret:…}` value in stored sandbox settings as this runtime's own proxy password rather than a reference the user wrote by hand. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract.\n\nReturns:\n Where this runtime keeps the sandbox proxy password.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + return SandboxProxySecretLocation.from_dict(await self._client.request("sandbox.proxySecret.getLocation", {}, **_timeout_kwargs(timeout))) + + async def _get(self, *, timeout: float | None = None) -> SandboxProxySecretGetResult: + "Reads the stored sandbox proxy password from the credential store `sandbox.proxySecret.getLocation` names, preferring the OS keychain and falling back to the on-disk store. Reports an absent value rather than failing when no entry exists. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract.\n\nReturns:\n The stored sandbox proxy password, if one is present.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + return SandboxProxySecretGetResult.from_dict(await self._client.request("sandbox.proxySecret.get", {}, **_timeout_kwargs(timeout))) + + async def _save(self, params: SandboxProxySecretSaveRequest, *, timeout: float | None = None) -> SandboxProxySecretSaveResult: + "Stores the sandbox proxy password in the credential store `sandbox.proxySecret.getLocation` names, replacing any existing entry. Reports whether the OS keychain accepted it or it fell back to the on-disk store. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract.\n\nArgs:\n params: The sandbox proxy password to store.\n\nReturns:\n Where the sandbox proxy password was stored.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return SandboxProxySecretSaveResult.from_dict(await self._client.request("sandbox.proxySecret.save", params_dict, **_timeout_kwargs(timeout))) + + async def _delete(self, *, timeout: float | None = None) -> None: + "Removes the stored sandbox proxy password from the credential store `sandbox.proxySecret.getLocation` names. Succeeds when no entry exists, so it is safe to call while clearing a proxy password whose origin is unknown. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + await self._client.request("sandbox.proxySecret.delete", {}, **_timeout_kwargs(timeout)) + + +# Experimental: this API group is experimental and may change or be removed. +class _InternalServerSandboxSettingsApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + + async def _apply_defaults(self, params: SandboxApplyDefaultsRequest, *, timeout: float | None = None) -> dict: + "Resolves a stored sandbox settings object into the effective one, filling every value the user left unset with its default. Read this to render or reason about the sandbox configuration a session would actually run under, rather than the sparse document the user wrote.\n\nArgs:\n params: The stored sandbox settings to resolve against the defaults.\n\nReturns:\n The effective sandbox settings, with every unset value resolved to its default.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return dict(await self._client.request("sandbox.settings.applyDefaults", params_dict, **_timeout_kwargs(timeout))) + + +# Experimental: this API group is experimental and may change or be removed. +class _InternalServerSandboxApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + self.proxy_secret = _InternalServerSandboxProxySecretApi(client) + self.settings = _InternalServerSandboxSettingsApi(client) + + async def _resolve_floored_config(self, params: _SandboxResolveFlooredConfigParams, *, timeout: float | None = None) -> SandboxResolveFlooredConfigResult: + "Raises a user's sandbox settings to an administrator's managed sandbox floor and returns the result a session should actually run under. The floor can only tighten: an enterprise force-on is reflected even when the user's own `enabled` is false, and a user cannot lower a setting the floor raised. The result is normalized through the declared sandbox shape, so a key reorder or an undeclared passthrough key is not mistaken for a policy change by callers that diff two results. A host must call this rather than merge the two layers itself, because reimplementing the direction of the fold is what turns an administrator's floor into a suggestion.\n\nArgs:\n params: The user's sandbox settings and the managed sandbox floor to raise them to.\n\nReturns:\n The user's sandbox settings raised to the managed floor.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return SandboxResolveFlooredConfigResult.from_dict(await self._client.request("sandbox.resolveFlooredConfig", params_dict, **_timeout_kwargs(timeout))) + + async def _prepare_picker_policy(self, params: _SandboxPreparePickerPolicyParams, *, timeout: float | None = None) -> _SandboxPreparePickerPolicyResult: + "Builds the effective filesystem policy one `@`-file picker checks its suggestions against, so Git discovery and grant canonicalization run once for the picker's lifetime instead of on every debounced keystroke. Returns no policy when the sandbox is absent or disabled, which the paired filter reads as \"keep every candidate\". A host must pair this with `sandbox.filterPickerCandidates` rather than resolve a grant list itself, because the picker's grants are narrower than a file tool's — the same path a built-in tool may read can be one the picker must not offer.\n\nArgs:\n params: The working directory and sandbox settings to build a file-picker policy from.\n\nReturns:\n The prepared file-picker policy, or none when the sandbox is disabled.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _SandboxPreparePickerPolicyResult.from_dict(await self._client.request("sandbox.preparePickerPolicy", params_dict, **_timeout_kwargs(timeout))) + + async def _filter_picker_candidates(self, params: _SandboxFilterPickerCandidatesParams, *, timeout: float | None = None) -> _SandboxFilterPickerCandidatesResult: + "Checks a batch of `@`-file picker suggestions against a policy prepared by `sandbox.preparePickerPolicy` and returns one allow decision per candidate, positionally aligned with the request. Batched deliberately: each check canonicalizes a path component-by-component through the real filesystem, so one call amortizes that over a single hop rather than dozens while the user types. A directory the policy does not itself allow can still be kept as a waypoint to an allowed subtree. An absent policy keeps every candidate.\n\nArgs:\n params: A batch of file-picker candidates to check against a prepared policy.\n\nReturns:\n Per-candidate allow decisions, positionally aligned with the request.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _SandboxFilterPickerCandidatesResult.from_dict(await self._client.request("sandbox.filterPickerCandidates", params_dict, **_timeout_kwargs(timeout))) + + async def _resolve_for_optional(self, params: _SandboxEffectiveForOptionalParams, *, timeout: float | None = None) -> _SandboxEffectiveForOptionalResult: + "The sandbox a single named feature actually launches under, which is not the same as the session's sandbox. A feature is routed out of the sandbox by its own flag — `sandboxMcpServers`, `sandboxLspServers` — so an enabled session can still start one server confined and another free. Returns a disabled sandbox rather than an error when the host has no sandbox slice at all, so an absent config and an opted-out feature read alike at the launch site. A host must ask per feature rather than reuse the session config, because reading only `enabled` confines a server the user opted out of, or reports as sandboxed one that is not.\n\nArgs:\n params: A sandbox config and the per-feature routing flag to resolve it for.\n\nReturns:\n The sandbox a named feature actually launches under.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _SandboxEffectiveForOptionalResult.from_dict(await self._client.request("sandbox.resolveForOptional", params_dict, **_timeout_kwargs(timeout))) + + async def _check_platform_support(self, *, timeout: float | None = None) -> bool: + "Reports whether this machine can run an OS-level command sandbox at all. A host must ask before it offers sandboxing, because the answer is a property of the machine rather than of any setting: on Linux it depends on bubblewrap being installed, and on Windows on the OS build supporting BaseContainer. Probing it requires spawning a process the first time, so the answer is cached for the life of the runtime. A host that assumes support and enables the sandbox anyway produces a session in which every shell command fails.\n\nReturns:\n True when an OS-level sandbox can run on this host.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + return bool(await self._client.request("sandbox.checkPlatformSupport", {}, **_timeout_kwargs(timeout))) + + async def _check_windows_host_loopback_support(self, *, timeout: float | None = None) -> bool: + "Reports whether this Windows host's BaseContainer implementation can reach the host's loopback interface directly, which is what a local-network policy needs to avoid routing through the egress proxy. Like `sandbox.checkPlatformSupport`, the answer is a property of the machine's OS build rather than of any setting, and resolving it the first time spawns a platform probe. A host that offers the direct local-network option on a machine that lacks the capability produces a session whose sandboxed commands cannot reach local services. Always false off Windows.\n\nReturns:\n True when the sandbox on this host can reach host loopback directly.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + return bool(await self._client.request("sandbox.checkWindowsHostLoopbackSupport", {}, **_timeout_kwargs(timeout))) + + async def _get_unsupported_message(self, *, timeout: float | None = None) -> str: + "The explanation a host shows when it refuses an explicit request to turn the sandbox on for a machine that cannot run one. Carries the platform-specific remedy — install bubblewrap, update Windows — and the documentation link, so a host that got `false` from `sandbox.checkPlatformSupport` does not have to compose its own reason and cannot describe the same machine differently from the runtime's own spawn path.\n\nReturns:\n The user-facing refusal, including the platform remedy and the documentation link.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + return str(await self._client.request("sandbox.getUnsupportedMessage", {}, **_timeout_kwargs(timeout))) + + async def _get_unsupported_setting_disabled_notice(self, *, timeout: float | None = None) -> str: + "The notice a host shows when a stored `sandbox` setting is being ignored for this session because the machine cannot enforce it. Distinct from `sandbox.getUnsupportedMessage`, which refuses an explicit request: this one reports that a setting the user already saved is inert, states that commands will run unsandboxed, and confirms the stored setting itself is left alone. A host that stays silent here leaves the user believing a sandbox is active.\n\nReturns:\n The user-facing notice that a saved sandbox setting is inert for this session.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + return str(await self._client.request("sandbox.getUnsupportedSettingDisabledNotice", {}, **_timeout_kwargs(timeout))) + + async def _get_unsupported_startup_notice(self, params: _SandboxUnsupportedStartupNoticeParams, *, timeout: float | None = None) -> _SandboxUnsupportedStartupNoticeResult: + "The warning a host prints at startup when a session asked for a sandbox on a machine that cannot run one. The third of the unsupported notices, and the only one that depends on the policy in force: it names the remedy the reader can actually act on, which is the stored setting for an unmanaged host, an administrator for a managed one, and waiting for the policy to resolve when the sandbox stayed on only because the policy could not be determined. Returns no notice when the machine does support a sandbox, so a host can call it unconditionally on the startup path. A host that composes its own warning tells a managed user to change a setting their policy will overwrite.\n\nArgs:\n params: The managed sandbox policy in force, which selects which remedy the notice offers.\n\nReturns:\n The startup warning, absent when the machine can run a sandbox.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _SandboxUnsupportedStartupNoticeResult.from_dict(await self._client.request("sandbox.getUnsupportedStartupNotice", params_dict, **_timeout_kwargs(timeout))) + + async def _check_enforcement_command(self, params: _SandboxEnforcementCommandAllowedParams, *, timeout: float | None = None) -> bool: + "Whether one slash command stays reachable while sandbox enforcement is unavailable and the session is otherwise blocked. The permitted set is deliberately narrow — commands that only report state, and the few that let the user repair or leave the situation — and it is keyed on the arguments as well as the name, so `/sandbox status` is admitted where `/sandbox enable` is not. A host must ask rather than keep its own list: a command this returns false for is one the runtime would refuse anyway, and a divergent list either strands the user with no way out or admits a command that acts on an unenforced session.\n\nArgs:\n params: One slash command, as typed, to test against the enforcement-block allowlist.\n\nReturns:\n True when the command stays available while enforcement is blocked.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return bool(await self._client.request("sandbox.checkEnforcementCommand", params_dict, **_timeout_kwargs(timeout))) + + async def _get_docs_url(self, *, timeout: float | None = None) -> SandboxDocsURLResult: + "Returns the documentation URL a host should point a user at when it declines or downgrades a sandbox setting. The runtime owns the address so every surface cites the same page and a host does not hard-code a link that later moves.\n\nReturns:\n The documentation URL a host cites when it declines or downgrades a sandbox setting.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + return SandboxDocsURLResult.from_dict(await self._client.request("sandbox.getDocsUrl", {}, **_timeout_kwargs(timeout))) + + +# Experimental: this API group is experimental and may change or be removed. +class _InternalServerPersistenceApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + + async def _write_raw_json_locked(self, params: PersistenceWriteRawJSONLockedRequest, *, timeout: float | None = None) -> PersistenceWriteRawJSONLockedResult: + "Atomically replaces a JSON file's entire contents while holding the runtime's per-path lock. The lock lives in the runtime process, so a host that writes these files itself takes no part in it and can tear a concurrent runtime write; route the write here instead. Unlike a merging write this never reads or parses the existing file, so a previously corrupted snapshot is replaced wholesale rather than failing every later write. Use it only for callers that always persist a complete snapshot.\n\nArgs:\n params: A complete JSON snapshot to write under the runtime's per-path file lock.\n\nReturns:\n The outcome of a locked snapshot write.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return PersistenceWriteRawJSONLockedResult.from_dict(await self._client.request("persistence.writeRawJsonLocked", params_dict, **_timeout_kwargs(timeout))) + + +# Experimental: this API group is experimental and may change or be removed. +class _InternalServerUserSettingsApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + + async def _set_hook_disabled(self, params: _UserSettingsSetHookDisabledRequest, *, timeout: float | None = None) -> _UserSettingsSetHookDisabledResult: + "Adds or removes one hook key in the user `disabledHooks` setting, rewriting settings.json in place and leaving every other setting untouched. Reports a typed outcome rather than failing the call, so a host can tell an unparseable settings file apart from a write that could not complete.\n\nArgs:\n params: One hook to disable or re-enable.\n\nReturns:\n Outcome of rewriting the `disabledHooks` setting.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _UserSettingsSetHookDisabledResult.from_dict(await self._client.request("user.settings.setHookDisabled", params_dict, **_timeout_kwargs(timeout))) + + async def _load_for_config_dir(self, params: _UserSettingsLoadForConfigDirRequest, *, timeout: float | None = None) -> dict: + "Reads the user's settings document exactly as `user.settings.load` does, but from a caller-supplied configuration directory instead of the one the server resolved for itself. Use this when a consumer scopes a session to its own Copilot home — the SDK's per-session `configDir` override — so the settings read match the directory that session actually uses. An absent or empty `configDir` resolves the server's own home, making this identical to `user.settings.load`. Hook-validation relaxations are deliberately not caller-supplied: they continue to come from the server's environment, so a consumer cannot widen the host's hook policy through this method.\n\nArgs:\n params: Selects the configuration directory whose user settings to read.\n\nReturns:\n The stored user settings document, keyed by setting name.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return dict(await self._client.request("user.settings.loadForConfigDir", params_dict, **_timeout_kwargs(timeout))) + + async def _load_repo(self, params: UserSettingsLoadRepoRequest, *, timeout: float | None = None) -> UserSettingsLoadRepoResult: + "Reads a repository's checked-in settings document as stored, normalized, together with any diagnostic raised while reading it. This is the repository half of the layering `user.settings.get` reports: `user.settings.load` reads the user's own document, and this reads the one a repository checks in to govern the keys `user.settings.governanceKeys` lists as repo-governable. A document that fails to parse, or that sets a key a repository may not govern, is reported through `warning` while the settings that did load are still returned, so a consumer surfaces the diagnostic and continues rather than failing the session. When `repoRoot` is the user's home directory the Claude-compatible configuration is skipped, so a home-directory session does not inherit a personal config as repository policy.\n\nArgs:\n params: Selects the repository whose checked-in settings to read.\n\nReturns:\n A repository's settings as stored, together with any diagnostic raised while reading them.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return UserSettingsLoadRepoResult.from_dict(await self._client.request("user.settings.loadRepo", params_dict, **_timeout_kwargs(timeout))) + + async def _load_and_merge_repo(self, params: _UserSettingsLoadAndMergeRepoRequest, *, timeout: float | None = None) -> _UserSettingsLoadAndMergeRepoResult: + "Reads a repository's settings and merges them over a supplied user layer in one step, returning the settings a consumer should act on inside that repository. Use this rather than reading the two layers separately: the merge is per setting and collection-aware, so a repository adds to the collections it is allowed to influence without replacing the user's own entries, and a whole-document overlay would get that wrong. Any diagnostic raised while reading the repository layer is returned rather than printed, so the consumer decides how to surface it; the settings that did load are merged regardless.\n\nArgs:\n params: A repository to read settings from, and the user layer they are merged over.\n\nReturns:\n The settings a consumer should act on inside a repository, together with any diagnostic raised while reading the repository layer.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _UserSettingsLoadAndMergeRepoResult.from_dict(await self._client.request("user.settings.loadAndMergeRepo", params_dict, **_timeout_kwargs(timeout))) + + async def _get_path(self, *, timeout: float | None = None) -> UserSettingsPathResult: + "Reports the on-disk path of the settings document `user.settings.load` and `user.settings.set` read and write. Resolved the same way those methods resolve it, so a host that shows or opens the settings file names the file the runtime actually uses, including the extensionless legacy `settings` file when that is what is present.\n\nReturns:\n The on-disk location of the user's settings document.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + return UserSettingsPathResult.from_dict(await self._client.request("user.settings.getPath", {}, **_timeout_kwargs(timeout))) + + async def _load_with_warning(self, *, timeout: float | None = None) -> UserSettingsLoadWithWarningResult: + "Reads the user's stored settings together with the diagnostic raised while reading them. This is `user.settings.load` plus the reason part of the document was ignored: that one answers the document alone, so a caller cannot tell a clean read from a salvaged one. Read this when the caller is about to write the file back, where accepting a partial parse would discard every value the loader could not read. The settings that did load are still reported alongside the warning, so a consumer may surface it and continue instead of failing.\n\nReturns:\n The user's stored settings, together with any diagnostic raised while reading them.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + return UserSettingsLoadWithWarningResult.from_dict(await self._client.request("user.settings.loadWithWarning", {}, **_timeout_kwargs(timeout))) + + async def _merge_managed_and_user(self, params: _UserSettingsMergeManagedAndUserParams, *, timeout: float | None = None) -> UserSettingsMergeManagedAndUserResult: + "Folds a user settings layer over a managed (enterprise policy) layer and returns the merged settings. The managed layer's governance keys win, `autoTier` resolves through its overridable form, and the two per-entry control maps (`enabledPlugins`, `extraKnownMarketplaces`) union across the layers and are then stamped back from the managed layer, so each entry an administrator declares wins while the user keeps control of the rest. The parameters are named for their layers rather than being a generic base/override pair: nothing about a settings object identifies which layer produced it, so the runtime cannot detect a transposed call, and transposing one resolves the governance keys in the user's favour instead of the administrator's. Supplying only one layer returns it unchanged, and supplying neither returns an empty object. Rejects a managed `autoTier` that is neither a user-configurable tier nor a single-key overridable form, rather than silently dropping it.\n\nArgs:\n params: The managed (enterprise policy) layer and the user layer to fold together. The field names carry the layer identity: nothing about a settings object identifies which layer produced it, so a generic base/override pair would let a caller transpose them and silently resolve the governance keys in the user's favour.\n\nReturns:\n The user layer folded over the managed layer.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return UserSettingsMergeManagedAndUserResult.from_dict(await self._client.request("user.settings.mergeManagedAndUser", params_dict, **_timeout_kwargs(timeout))) + + async def _merge_repo_value(self, params: _UserSettingsMergeRepoValueParams, *, timeout: float | None = None) -> UserSettingsMergeRepoValueResult: + "Folds a repository's settings layer into the user layer. Each repo-overridable key carries a directional policy chosen so a repository can only add restrictions and never relax the user's own. The two per-entry managed control maps are the exception those directional rules cannot express — a repository may legitimately enable plugins and declare marketplaces — so they are re-stamped afterwards from the runtime's ambient managed policy. That makes the result depend on the runtime process's managed state and not on the arguments alone: a host must call this rather than reimplement the fold, or a repository could relax a restriction the administrator imposed. Rejects when either layer is not a JSON object.\n\nArgs:\n params: The user layer and the repository layer to fold together.\n\nReturns:\n The repository layer folded into the user layer.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return UserSettingsMergeRepoValueResult.from_dict(await self._client.request("user.settings.mergeRepoValue", params_dict, **_timeout_kwargs(timeout))) + + async def _migrate_staff_update_channel_once(self, params: UserSettingsHostContext, *, timeout: float | None = None) -> _UserSettingsMigrateStaffUpdateChannelOnceResult: + "Runs the one-time migration that moves a staff account's update channel to stable, and records that it ran. Idempotent and self-gating: it takes a lock over the global-state file, re-reads the completion stamp inside it, and returns `current` rather than migrating twice, so two hosts racing at startup cannot both migrate. Only a `migrated` status means the persisted settings changed and the caller should reload them. An account on the experimental channel is deliberately left alone and reported as `excluded-experimental`.\n\nArgs:\n params: The filesystem and environment facts a user-settings file is resolved from. Passed explicitly rather than read from the runtime's own process, so a host whose configuration directory differs from its process environment resolves the file it means.\n\nReturns:\n The outcome of the one-time staff update-channel migration.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _UserSettingsMigrateStaffUpdateChannelOnceResult.from_dict(await self._client.request("user.settings.migrateStaffUpdateChannelOnce", params_dict, **_timeout_kwargs(timeout))) + + async def _reset_staff_model_selection_once(self, params: _UserSettingsResetStaffModelSelectionOnceRequest, *, timeout: float | None = None) -> _UserSettingsResetStaffModelSelectionOnceResult: + "Runs the one-time reset that clears a staff account's pinned model selection, and reports the timestamp the host should record. Idempotent and self-gating: the reset is gated on a fixed cutoff, so a caller that passes back the timestamp it recorded gets `didReset: false` rather than a second reset. It applies to staff accounts only; any other account is reported as not reset without the settings document being opened. Only `didReset: true` means the persisted settings changed, and only then are the updated settings returned for the host to adopt.\n\nArgs:\n params: Selects the configuration directory to run the one-time staff model-selection reset against, and supplies the caller's record of whether it has already run.\n\nReturns:\n The outcome of the one-time staff model-selection reset.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _UserSettingsResetStaffModelSelectionOnceResult.from_dict(await self._client.request("user.settings.resetStaffModelSelectionOnce", params_dict, **_timeout_kwargs(timeout))) + + async def _get_unknown_keys_warning(self, params: _UserSettingsUnknownKeysWarningRequest, *, timeout: float | None = None) -> _UserSettingsUnknownKeysWarningResult: + "Reports the top-level keys in the user settings document that the runtime does not recognize, phrased for display to the user. This is a separate question from the warning `user.settings.loadWithWarning` returns: that one describes parts of the document the loader had to ignore to load the rest, whereas an unknown key parses cleanly and is simply not a setting, so a host that surfaces only the load warning silently accepts a misspelled key that will never take effect. Nothing is reported when the document is absent, unreadable, or failed to load, because an unknown-key list derived from a document the runtime could not read would be misleading rather than helpful. Inspection never modifies the document.\n\nArgs:\n params: Selects the configuration directory whose user settings document should be inspected for unrecognized top-level keys.\n\nReturns:\n The unrecognized top-level keys found in the user settings document, already phrased for display.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _UserSettingsUnknownKeysWarningResult.from_dict(await self._client.request("user.settings.getUnknownKeysWarning", params_dict, **_timeout_kwargs(timeout))) + + async def _build_runtime(self, params: _UserSettingsBuildRuntimeRequest, *, timeout: float | None = None) -> dict: + "Resolves the effective runtime settings for a host that is about to start a session, layering the given overrides over the settings documents, the environment and the platform defaults. A host cannot assemble this itself: the precedence between a user document, a repository document, the managed layers and the environment is the runtime's own rule, and a host that re-implements it drifts from the server it is configuring. The working directory, environment and platform are taken as arguments rather than read from the server process, so a host resolving settings for a directory other than its own gets that directory's answer. Nothing is written; this only computes.\n\nArgs:\n params: The overrides and host context to resolve effective runtime settings from.\n\nReturns:\n The effective runtime settings, keyed by setting name. Keys this runtime does not recognize are preserved rather than dropped, so a caller reading a document written by a newer runtime keeps every value.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return dict(await self._client.request("user.settings.buildRuntime", params_dict, **_timeout_kwargs(timeout))) + + async def _migrate_staff_log_level_once(self, params: _UserSettingsMigrateStaffLogLevelOnceRequest, *, timeout: float | None = None) -> _UserSettingsMigrateStaffLogLevelOnceResult: + "Runs the one-time migration that moves a staff account's log level to the current default, and records that it ran. Idempotent and self-gating: it takes a lock over the global-state file and re-reads the completion stamp inside it, so two hosts racing at startup cannot both migrate. Only `refreshed` can have changed the persisted settings, and the settings it returns are the document re-read after the write, so a host should adopt them rather than reloading separately and racing the next writer.\n\nArgs:\n params: Selects the configuration directory to run the one-time staff log-level migration against, and supplies the caller's record of whether it has already run.\n\nReturns:\n The outcome of the one-time staff log-level migration.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _UserSettingsMigrateStaffLogLevelOnceResult.from_dict(await self._client.request("user.settings.migrateStaffLogLevelOnce", params_dict, **_timeout_kwargs(timeout))) + + async def _migrate_obsolete_on_disk(self, params: _UserSettingsMigrateObsoleteOnDiskRequest, *, timeout: float | None = None) -> _UserSettingsMigrateObsoleteOnDiskResult: + "Runs the one-time cleanup that removes obsolete CLI-owned settings and legacy rubber-duck keys from the user's settings document, and reports what it did. Idempotent and safe to call on every startup: with nothing obsolete left it reports `unchanged` and writes nothing. The cleanup preserves every key it does not recognize, so a setting written by a newer host is never dropped by an older one, and a document that fails to parse is reported `invalid` and left untouched rather than rewritten from a partial read. The cleaned document is not returned: it is written before this replies, so `user.settings.load` reads it authoritatively. A host that surfaces unrecognized keys through `user.settings.unknownKeys` wants this first, because the keys this retires would otherwise be shown to the user as if they were misspelled settings.\n\nArgs:\n params: Selects the configuration directory whose settings document to clean.\n\nReturns:\n The outcome of the one-time obsolete-settings cleanup.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _UserSettingsMigrateObsoleteOnDiskResult.from_dict(await self._client.request("user.settings.migrateObsoleteOnDisk", params_dict, **_timeout_kwargs(timeout))) + + async def _get_paths(self, params: UserSettingsGetPathsRequest, *, timeout: float | None = None) -> UserSettingsGetPathsResult: + "Resolves the on-disk locations of the settings files this runtime reads, so a consumer can show or open them. The user path is the same file `user.settings.get`, `user.settings.load`, and `user.settings.set` operate on. Supply `repoRoot` to also resolve that repository's checked-in and local settings paths. A path is reported whether or not the file exists.\n\nArgs:\n params: Selects which settings file paths to resolve.\n\nReturns:\n On-disk locations of the settings files this runtime reads. A path is reported whether or not the file exists.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return UserSettingsGetPathsResult.from_dict(await self._client.request("user.settings.getPaths", params_dict, **_timeout_kwargs(timeout))) + + async def _list_governance_keys(self, *, timeout: float | None = None) -> UserSettingsGovernanceKeysResult: + "Lists the top-level user-setting keys by who is allowed to govern them: `repo` keys a repository may override through `.github/copilot/settings.json`, `claudeRepo` keys a repository may override through a Claude-compatible config, and `managed` keys an enterprise policy may fix. The lists are static for a given runtime build and fail closed: a key absent from a list cannot be governed that way.\n\nReturns:\n Top-level user-setting keys grouped by who may govern them.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + return UserSettingsGovernanceKeysResult.from_dict(await self._client.request("user.settings.listGovernanceKeys", {}, **_timeout_kwargs(timeout))) + + async def _edit(self, params: _UserSettingsEditRequest, *, timeout: float | None = None) -> _SettingsEditResult: + "Atomically edits one canonical user setting inside its settings-file lock. Reads, canonicalizes touched aliases, applies the edit, validates the resulting top-level setting and writes before releasing the lock. An explicit autoUpdatesChannel set completes the staff migration in the same transaction. Only a malformed existing file returns `invalid`; refused keys, invalid values and I/O errors reject the call.\n\nArgs:\n params: One atomic edit of a canonical setting in settings.json.\n\nReturns:\n Outcome of an atomic user or repository settings edit.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = params.to_dict() + return _SettingsEditResult.from_dict(await self._client.request("user.settings.edit", params_dict, **_timeout_kwargs(timeout))) + + async def _normalize(self, params: UserSettingsNormalizeRequest, *, timeout: float | None = None) -> UserSettingsNormalizeResult: + "Rewrites legacy and snake_case setting names in a settings object to their canonical spelling. A caller editing settings.json must normalize before writing: a legacy key left in place survives the write, is re-normalized on the next load, and silently shadows the canonical value the write just stored.\n\nArgs:\n params: Settings object whose key names should be canonicalized.\n\nReturns:\n Settings object with canonical key names.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return UserSettingsNormalizeResult.from_dict(await self._client.request("user.settings.normalize", params_dict, **_timeout_kwargs(timeout))) + + async def _diagnose_problems(self, params: UserSettingsProblemsRequest, *, timeout: float | None = None) -> UserSettingsProblemsResult: + "Diagnoses a set of already-loaded settings layers, reporting each key that will not take effect and why. Settings load tolerantly so that one bad key does not stop a session from starting; this method surfaces what was dropped so a caller can report it instead of silently ignoring the user's intent. Takes the layers as given rather than reading them, so a caller can diagnose exactly the content it is showing, including unsaved edits. Never echoes a setting's value, because a value may be a token or another secret. Marked internal because it exists to carry a CLI call site off the native addon, not to serve a consumer-facing need; it becomes public once an SDK consumer needs to inspect settings health itself.\n\nArgs:\n params: The settings layers to diagnose.\n\nReturns:\n The settings that will not take effect across the given layers.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return UserSettingsProblemsResult.from_dict(await self._client.request("user.settings.diagnoseProblems", params_dict, **_timeout_kwargs(timeout))) + + async def _apply_top_key(self, params: UserSettingsApplyTopKeyRequest, *, timeout: float | None = None) -> UserSettingsApplyTopKeyResult: + "Validates one top-level setting subtree and writes just that key to settings.json as a single step. Doing both here closes the race a caller cannot close itself: between a separate preflight read and the write, the file may change. Rewriting only the touched key keeps legacy siblings out of settings.json. Answers a status rather than failing, so a validation problem and a write problem stay distinguishable.\n\nArgs:\n params: A single top-level setting subtree to validate and write.\n\nReturns:\n Outcome of validating and writing one top-level setting subtree.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = params.to_dict() + return UserSettingsApplyTopKeyResult.from_dict(await self._client.request("user.settings.applyTopKey", params_dict, **_timeout_kwargs(timeout))) + + async def _write_all(self, params: UserSettingsWriteAllRequest, *, timeout: float | None = None) -> None: + "Replaces the user settings file's entire content in one write. A key absent from `settings` is removed, which is what distinguishes this from `user.settings.set`: that one merges, so it can never clear a key the caller has dropped. For a caller saving the result of a raw edit, a merge would silently resurrect every key the user deleted. Records the same one-time mutation marks a per-key write records, so a setting the user has now chosen explicitly stops being migrated for them.\n\nArgs:\n params: The whole content to store in the user settings file.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + await self._client.request("user.settings.writeAll", params_dict, **_timeout_kwargs(timeout)) + + async def _update(self, params: _UserSettingsUpdateRequest, *, timeout: float | None = None) -> _UserSettingsUpdateResult: + "Merges a partial object into one top-level user setting, re-reading the stored value inside this runtime process's settings.json write lock so that concurrent writers in this process to different sub-fields of the same key do not clobber each other. Unlike `user.settings.set`, which replaces a key's whole value with what the caller last read, this keeps sub-fields the caller did not name when both the stored value and supplied value are objects; otherwise the supplied value replaces the stored value. Use it whenever two independent surfaces own different sub-fields of one setting. Returns the key's stored value after the write, in `value`.\n\nArgs:\n params: Names the user setting to merge into, and the partial object to merge.\n\nReturns:\n The stored value of the user setting after the merge.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _UserSettingsUpdateResult.from_dict(await self._client.request("user.settings.update", params_dict, **_timeout_kwargs(timeout))) + + async def _load_for_edit(self, *, timeout: float | None = None) -> UserSettingsLoadForEditResult: + "Reads settings.json as stored for a caller that is about to edit it. Unlike `user.settings.get`, this returns file values rather than per-key metadata and defaults. Reports `missing` for an absent file and `invalid` when the existing file will not parse. Retired config.json and extensionless files are not read.\n\nReturns:\n Contents of settings.json as stored, for a caller about to rewrite it.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + return UserSettingsLoadForEditResult.from_dict(await self._client.request("user.settings.loadForEdit", {}, **_timeout_kwargs(timeout))) + + async def _validate(self, params: UserSettingsValidateRequest, *, timeout: float | None = None) -> UserSettingsValidateResult: + "Validates one top-level setting subtree without writing it, reporting each problem with the path it applies to. Scoped to a single top-level key on purpose: validating the whole file would reject an edit whenever an unrelated pre-existing field is malformed, locking the user out of the very tool that could fix it.\n\nArgs:\n params: A single top-level setting subtree to validate.\n\nReturns:\n Outcome of validating one top-level setting subtree.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return UserSettingsValidateResult.from_dict(await self._client.request("user.settings.validate", params_dict, **_timeout_kwargs(timeout))) + + +# Experimental: this API group is experimental and may change or be removed. +class _InternalServerUserApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + self.settings = _InternalServerUserSettingsApi(client) + + +# Experimental: this API group is experimental and may change or be removed. +class _InternalServerRepoSettingsApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + + async def _edit(self, params: _RepoSettingsEditRequest, *, timeout: float | None = None) -> _SettingsEditResult: + "Atomically edits one allowed top-level repository setting. Reads the raw file, preserves unrelated keys, applies one scalar or list edit, validates the result and writes under one file lock, so concurrent sibling or list edits are not lost. A malformed or unsafe existing file returns `invalid` without a write; rejected keys, invalid values and I/O errors reject the call.\n\nArgs:\n params: One atomic edit of an allowed top-level repository setting.\n\nReturns:\n Outcome of an atomic user or repository settings edit.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _SettingsEditResult.from_dict(await self._client.request("repo.settings.edit", params_dict, **_timeout_kwargs(timeout))) + + async def _read_raw(self, params: _RepoSettingsReadRawRequest, *, timeout: float | None = None) -> _RepoSettingsReadRawResult: + "Reads one repository settings file without settings normalization or layering. The existing JSONC parser accepts comments and trailing commas and applies its prototype-safety rules. Missing or blank files return an empty valid config. Invalid files return an empty config with invalid=true; other read failures reject.\n\nArgs:\n params: Which repository settings file to read.\n\nReturns:\n Parsed repository settings without normalization or layering.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _RepoSettingsReadRawResult.from_dict(await self._client.request("repo.settings.readRaw", params_dict, **_timeout_kwargs(timeout))) + + async def _write_key(self, params: _RepoSettingsWriteKeyRequest, *, timeout: float | None = None) -> None: + "Writes or removes one allowed top-level repository setting through the existing validator and file lock. Preserves unrelated stored keys. Invalid keys, values, existing documents, and I/O errors reject without replacing the file. Deletion requires delete=true; otherwise value must be present.\n\nArgs:\n params: One key to write or remove in a repository settings file.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = params.to_dict() + await self._client.request("repo.settings.writeKey", params_dict, **_timeout_kwargs(timeout)) + + async def _write_all(self, params: _RepoSettingsWriteAllRequest, *, timeout: float | None = None) -> None: + "Replaces the repository-overridable settings in one locked write. Allowed keys present in settings are validated and written; allowed keys absent from settings are removed. Other input keys are ignored, and unknown keys already stored in the file remain unchanged. Invalid values or an invalid existing file reject without a partial write.\n\nArgs:\n params: The complete set of repository-overridable settings to store.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + await self._client.request("repo.settings.writeAll", params_dict, **_timeout_kwargs(timeout)) + + +# Experimental: this API group is experimental and may change or be removed. +class _InternalServerRepoApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + self.settings = _InternalServerRepoSettingsApi(client) + + +# Experimental: this API group is experimental and may change or be removed. +class _InternalServerFeatureFlagsApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + + async def _resolve_startup_gates(self, params: _FeatureFlagsResolveStartupGatesRequest, *, timeout: float | None = None) -> dict: + "Resolves named gates against this installation's cached online-experiment assignment, for the account the runtime is already authenticated as. Unlike `featureFlags.resolve`, which layers static availability with the populations supplied, this reads the experiment assignment the runtime cached for its own current authentication, so a launch can honor a rollout before any session exists. The account is never taken from the request: naming one would read another account's cached assignments. The cached assignment answers immediately and any network refresh happens behind it, so this call does not wait on the network. Not available to an extension, which cannot carry the user's consent to read their assignment.\n\nArgs:\n params: Caller population and startup inputs to resolve named online-experiment gates for. Carries no account: the runtime resolves the assignment for the authentication it already holds.\n\nReturns:\n The resolved gates, keyed by flag name.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return dict(await self._client.request("featureFlags.resolveStartupGates", params_dict, **_timeout_kwargs(timeout))) + + async def _list_metadata(self, *, timeout: float | None = None) -> _FeatureFlagsMetadataResult: + "Lists every feature flag this runtime declares, with the population each one is declared for, its experimental description, and the online-experiment flag it maps to. This is the static catalog: it describes how each flag is defined, not whether it is on for anyone. Use `featureFlags.resolve` for static values with caller overrides, not active runtime values or experiment assignments.\n\nReturns:\n Every feature flag this runtime declares, with the shape each one was declared with.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + return _FeatureFlagsMetadataResult.from_dict(await self._client.request("featureFlags.listMetadata", {}, **_timeout_kwargs(timeout))) + + async def _resolve(self, params: _FeatureFlagsResolveRequest, *, timeout: float | None = None) -> dict: + "Computes static feature flags from the supplied population and overrides without changing runtime state or verifying audience membership. Starts from declared availability, then applies direct feature-flag environment values, user settings, and explicit `flagOverrides`. It does not apply `COPILOT_EXP_*` experiment assignments, authenticated startup gates, or active runtime state. Settings include `enabledFeatureFlags`, the legacy `feature_flags.enabled` list, and supported convenience settings. Supply `baseFlags` to use an existing flag set instead of declared availability. Omit `isStaffGitHub` or `isStaffMicrosoft` when membership was never recorded: a legacy staff account then counts as both populations.\n\nArgs:\n params: Caller population and overrides for static feature-flag resolution, independent of experiment assignments and active runtime state.\n\nReturns:\n The resolved feature flags, keyed by flag name.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return dict(await self._client.request("featureFlags.resolve", params_dict, **_timeout_kwargs(timeout))) + + +# Experimental: this API group is experimental and may change or be removed. +class _InternalServerPermissionsLocationsApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + + async def _cleanup_orphaned(self, *, timeout: float | None = None) -> _LocationPermissionsCleanupResult: + "Drops stored per-location permission grants whose directory no longer exists, and reports which location keys were removed. Runs against the permission store under the runtime's own Copilot home, the same store `session.permissions.locations.resolve` reads. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract; it is migration plumbing, not a surface consumers are meant to depend on. It can become public once an SDK consumer needs to prune its own permission store.\n\nReturns:\n Which stored location permissions were dropped because their directory is gone.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + return _LocationPermissionsCleanupResult.from_dict(await self._client.request("permissions.locations.cleanupOrphaned", {}, **_timeout_kwargs(timeout))) + + +# Experimental: this API group is experimental and may change or be removed. +class _InternalServerPermissionsFolderTrustApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + + async def _check_trust(self, params: _FolderTrustIsTrustedParams, *, timeout: float | None = None) -> FolderTrustCheckResult: + "Reports whether a folder is trusted, reading the persisted trusted-folders list directly rather than through a session. Use this from a host that has no session yet, such as a one-shot command deciding whether to honor a checkout's own settings. It differs from the session-scoped check in two ways that matter: it takes the configuration directory to read the list from, instead of inheriting the session's, and it answers only from the persisted list, so trust a user granted for the lifetime of one session is not visible here. A host that has a session should prefer the session-scoped check, which also covers session-only trust.\n\nArgs:\n params: A folder to check against a persisted trusted-folders list, and the configuration directory holding that list.\n\nReturns:\n Folder trust check result.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return FolderTrustCheckResult.from_dict(await self._client.request("permissions.folderTrust.checkTrust", params_dict, **_timeout_kwargs(timeout))) + + +# Experimental: this API group is experimental and may change or be removed. +class _InternalServerPermissionsApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + self.locations = _InternalServerPermissionsLocationsApi(client) + self.folder_trust = _InternalServerPermissionsFolderTrustApi(client) + + async def _evaluate_managed_policy(self, params: _PermissionsEvaluateManagedPolicyParams, *, timeout: float | None = None) -> PermissionsEvaluateManagedPolicyResult: + "Interprets a verbatim managed `permissions` slice into the three policy questions a host asks before granting elevated permissions: whether bypass-permissions mode is forbidden, whether a policy exists that this build could not determine, and whether the layer supplies permission rules the session must enforce. An absent or empty slice means no managed policy applies. The runtime and its permission service share one policy type, so a new managed permission setting is interpreted in exactly one place; a host that reimplements the reading would silently diverge the first time that set grows. Hosts must fail closed on an undetermined policy rather than treating it as permission granted.\n\nArgs:\n params: The verbatim managed `permissions` slice to interpret.\n\nReturns:\n The policy questions a host asks before granting elevated permissions.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return PermissionsEvaluateManagedPolicyResult.from_dict(await self._client.request("permissions.evaluateManagedPolicy", params_dict, **_timeout_kwargs(timeout))) + + +# Experimental: this API group is experimental and may change or be removed. +class _InternalServerManagedSettingsApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + + async def _read_device(self, *, timeout: float | None = None) -> _ManagedSettingsReadDeviceResult: + "Discovers device-managed settings the way the runtime's own device layer does, and reports the sandbox floor and the load outcome alongside the validated document. This differs from `managedSettings.read` in three ways a policy-enforcing host depends on: a document that validates to an empty object is reported as absent rather than as an empty policy, a discovery error does not suppress a document that still validates, and a failure is reported as a flag rather than only as error text. Hosts that compose the device layer into an effective policy use this, because `managedSettings.read` cannot distinguish \"no device policy\" from \"device policy could not be read\" and would silently hand the user's own layer the win.\n\nReturns:\n The device-managed settings layer, with the sandbox floor and the load outcome a host needs to compose it into an effective policy.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + return _ManagedSettingsReadDeviceResult.from_dict(await self._client.request("managedSettings.readDevice", {}, **_timeout_kwargs(timeout))) + + async def _retain_policy(self, params: _ManagedSettingsRetainPolicyParams, *, timeout: float | None = None) -> None: + "Retains the supplied managed layers as this runtime process's startup policy and re-applies the result to every live session. Hosts that own their own short-lived process policy use this, so a guarded mutation is checked against the policy the host actually fetched. Retaining is a whole-process replacement rather than a merge, so a host that already resolved a good policy must not retain a partial one over it. The failure flags are honest inputs rather than assumptions: reporting a fetch or load that failed keeps the layers already retained, and the interim fail-closed state, instead of clearing them.\n\nArgs:\n params: The managed layers to retain as this process's startup policy, with the outcome of fetching each one.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + await self._client.request("managedSettings.retainPolicy", params_dict, **_timeout_kwargs(timeout)) + + async def _apply_controls(self, params: _ManagedSettingsApplyControlsParams, *, timeout: float | None = None) -> _ManagedSettingsApplyControlsResult: + "Stamps an effective managed policy's individually-controlled entries onto a user settings document, returning the settings a host should actually act on. The managed layer wins on every plugin spec and marketplace name it names, and leaves everything it does not name as the user wrote it. Hosts use this rather than merging the two documents themselves, because the merge is per entry rather than per document and a whole-document overlay would either discard the user's unmanaged entries or let a user entry outrank a managed one. Omitting the managed layer returns the settings unchanged, which is what an unmanaged host wants. Rejects when either document is present but is not a JSON object rather than treating it as absent, since silently dropping a malformed policy hands the user's layer the win on exactly the entries the policy exists to control.\n\nArgs:\n params: A user settings document and the effective managed policy whose controlled entries should be stamped onto it.\n\nReturns:\n The user settings with the managed layer's controlled entries stamped on.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _ManagedSettingsApplyControlsResult.from_dict(await self._client.request("managedSettings.applyControls", params_dict, **_timeout_kwargs(timeout))) + + async def _parse_client_value(self, params: _ManagedSettingsParseClientValueParams, *, timeout: float | None = None) -> _ManagedSettingsParseClientValueResult: + "Validates the managed settings a client is supplying for a session it is about to open, and returns them reduced to the keys a client is allowed to set. A client-supplied policy is not the same thing as an administrator's: it may only narrow permissions, so this accepts `permissions` and rejects every other key outright rather than ignoring it. Rejecting is the point — a host that silently dropped an unsupported key would report a policy as applied while running without it. Hosts call this before opening the session rather than letting the open fail, so a malformed policy is reported against the request that carried it. The runtime applies the same rule again when the session starts, so this cannot be used to smuggle a wider document past it. An accepted document that sets nothing comes back as an empty object, which is distinct from supplying nothing at all.\n\nArgs:\n params: The client-supplied managed settings to validate and normalize. Omitting them is valid and yields no policy.\n\nReturns:\n The accepted managed settings, reduced to the keys a client is allowed to set.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _ManagedSettingsParseClientValueResult.from_dict(await self._client.request("managedSettings.parseClientValue", params_dict, **_timeout_kwargs(timeout))) + + async def _resolve_effective_value(self, params: _ManagedSettingsResolveEffectiveValueParams, *, timeout: float | None = None) -> _ManagedSettingsResolveEffectiveValueResult: + "Composes the supplied server, device/MDM, device sandbox floor and policy-helper layers into the single effective managed policy a host should enforce, resolving each key across the layers by the runtime's own precedence rules. Hosts that assemble managed layers themselves use this so their precedence matches the runtime's exactly instead of reimplementing it. The composition is not a pure function of its inputs: an advisory sandbox force-on that arrived over the server channel is declined when this host cannot run a sandbox, so the result reflects the runtime host's capabilities, and the returned settings carry `sandboxAdvisoryFloorDeclined: true` when that happened. Enforced channels (device/MDM and policy helper) are never declined, and a server force-on carrying `failIfUnavailable` is preserved so the session fails loudly instead of quietly running unsandboxed. Rejects when a supplied layer is present but is not a JSON object, rather than treating it as unmanaged, because silently dropping a malformed policy would hand the user's own layer the win on exactly the keys the policy exists to control.\n\nArgs:\n params: The managed-settings layers to compose into an effective policy. Every layer is optional; omitting all of them yields no effective policy.\n\nReturns:\n The effective managed policy composed from the supplied layers.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _ManagedSettingsResolveEffectiveValueResult.from_dict(await self._client.request("managedSettings.resolveEffectiveValue", params_dict, **_timeout_kwargs(timeout))) + + async def _resolve_bypass_disable_transition(self, params: _ManagedSettingsResolveBypassDisableTransitionParams, *, timeout: float | None = None) -> _ManagedSettingsResolveBypassDisableTransitionResult: + "Decides what a host must do to its bypass-permissions state after a managed-policy resolution lands, given the state already in force and the account now signed in. A host holds two pieces of state — which layer disabled bypass mode, and for a server-delivered disable, the account it was pinned to — and cannot decide the next step from the new policy alone. Three rules make that so, and each one is a control rather than a preference. A device/MDM disable outranks the account, so a later server answer never lifts it. An unknown policy, which is what a failed fetch or a refusal this build cannot enforce yields, preserves whatever is in force instead of reverting to permissive. And a server disable is lifted only when the account it was pinned to is no longer the signed-in one, which is what makes signing in to an unrestricted account recover without a restart while a reconnect on the same restricted account does not. Hosts call this rather than reimplementing it because getting the pinning backwards either strands a disable on an account that never carried one or drops one that still applies. `setDisabledState` is omitted when the host must leave its stored flag untouched, which is distinct from being told to set it to false.\n\nArgs:\n params: The bypass-permissions state a host holds today, the account now signed in, and what the managed-policy resolution said.\n\nReturns:\n The bypass-permissions state a host should hold after the resolution, and what it must do to get there.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _ManagedSettingsResolveBypassDisableTransitionResult.from_dict(await self._client.request("managedSettings.resolveBypassDisableTransition", params_dict, **_timeout_kwargs(timeout))) + + async def _ingest(self, params: _ManagedSettingsIngestParams, *, timeout: float | None = None) -> _ManagedSettingsIngestResult: + "Resolves managed (enterprise policy) settings for a host from every layer at once: the account's server-delivered policy, the device/MDM layer, and a platform policy helper. Hosts use this instead of fetching and composing the layers themselves, because the per-layer precedence and the fail-closed rules that apply when a layer cannot be confirmed are the runtime's own and must match the policy a session will later enforce. It does not retain or apply anything to process-global policy state, so a host may call it before any session exists — which is the point, since plugin activation and one-shot commands resolve policy before a session is available to carry it. The returned per-layer outcome flags are load-bearing rather than diagnostic: a host must distinguish an unmanaged device from a managed device whose policy could not be confirmed, and apply its fail-closed gates in the second case. Reports its diagnostics in `logMessages` rather than logging them, so a host that runs before a log sink exists still sees them.\n\nArgs:\n params: Inputs describing the host and account an ingestion should resolve managed policy for.\n\nReturns:\n The managed policy layers an ingestion resolved, together with the per-layer outcome flags a host needs to decide how much to trust the result. The flags are reported rather than folded into the settings because a host must be able to distinguish an unmanaged device from a managed device whose policy could not be confirmed, and apply its fail-closed gates in the second case.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _ManagedSettingsIngestResult.from_dict(await self._client.request("managedSettings.ingest", params_dict, **_timeout_kwargs(timeout))) + + async def _resolve_startup_policy(self, params: _ManagedSettingsResolveStartupPolicyParams, *, timeout: float | None = None) -> _ManagedSettingsResolveStartupPolicyResult: + "Resolves the managed (enterprise policy) settings a host must enforce from startup, and judges the account against the enterprise sign-in pin in the same pass. This composes the layers the way `managedSettings.ingest` does and then answers the two questions a host cannot answer for itself: which fail-closed floors apply when a layer could not be confirmed, and whether this credential is allowed to sign in at all. The sign-in verdict is the reason a host must not assemble this itself from `managedSettings.ingest` — a pinned organization is an access control, and a host that composed the layers but skipped the pin would let a developer sign in with a personal or another enterprise's account and escape every control that keys off the signed-in identity. The verdict is computed on every resolution, including one that is not retained, so declining to apply policy never widens access. Set `retainAndApply` to keep the resolved policy as this runtime process's startup policy; leave it false to resolve without side effects.\n\nArgs:\n params: Inputs describing the host and account a startup policy resolution should run for. Mirrors `managedSettings.ingest`, and adds the decision of whether the resolved policy is retained for this runtime process.\n\nReturns:\n The startup policy a resolution composed, the per-layer outcome flags a host needs to decide how much to trust it, and the enterprise sign-in verdict for the credential it ran against. The flags are reported rather than folded into the settings because a host must distinguish an unmanaged device from a managed device whose policy could not be confirmed, and apply its fail-closed gates in the second case.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _ManagedSettingsResolveStartupPolicyResult.from_dict(await self._client.request("managedSettings.resolveStartupPolicy", params_dict, **_timeout_kwargs(timeout))) + + async def _load_remote_control(self, params: _ManagedSettingsLoadRemoteControlParams, *, timeout: float | None = None) -> _ManagedSettingsLoadRemoteControlResult: + "Resolves the managed `remoteControl` policy for an account on its own, for a host deciding whether a remote-control session may start. Composes the same layers as `managedSettings.ingest` and returns only that one key, so a host does not have to know which layer won. An absent policy means the account is unmanaged for this capability and the host may proceed; that default is safe only while absence really does mean \"no policy\", so a service refusal that says an account policy exists which this client is too old to enforce resolves to an explicit disabled policy instead of to absence. A resolution that cannot be completed is rejected rather than answered as absence, for the same reason.\n\nArgs:\n params: Inputs describing the host and account whose managed remote-control policy should be resolved. Mirrors `managedSettings.ingest`, and deliberately carries no retention decision: reading this gate never becomes this runtime process's startup policy.\n\nReturns:\n The managed remote-control policy in force for an account, if any. An absent policy means the account is unmanaged for this capability, which a host reads as allowed — so absence here is a positive statement that no policy applies, never a stand-in for a resolution that failed. A resolution that cannot be completed is rejected instead.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _ManagedSettingsLoadRemoteControlResult.from_dict(await self._client.request("managedSettings.loadRemoteControl", params_dict, **_timeout_kwargs(timeout))) + + async def _resolve_device_force_login_orgs_gate(self, params: _ManagedSettingsResolveDeviceForceLoginOrgsGateParams, *, timeout: float | None = None) -> _ForceLoginOrgsDecision: + "Evaluates the device-channel enterprise sign-in pin (MDM plist, registry, or managed-settings file) against a credential, without resolving any server-delivered policy. This is the gate an entry point runs before it branches, and the only one that can refuse a developer's first sign-in: it is local, needs no credential, and a server-delivered pin only ever reaches accounts already inside the organization. It fails closed by construction — a discovery that cannot determine whether a pin exists refuses rather than allows, because a control that fails open under a triggerable condition is not a control. Hosts that also want the server channel should call `managedSettings.resolveStartupPolicy`, which folds both channels into one verdict.\n\nArgs:\n params: The credential and environment to evaluate the device-channel sign-in pin against.\n\nReturns:\n The verdict of the `forceLoginOrgs` enterprise sign-in pin for one credential. A managed device may pin Copilot to an approved set of GitHub organizations so a developer cannot sign in with a personal — or another enterprise's — account and escape every control that keys off the signed-in identity. A host must refuse to proceed when `allowed` is false; the remaining fields explain the refusal rather than qualifying it.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _ForceLoginOrgsDecision.from_dict(await self._client.request("managedSettings.resolveDeviceForceLoginOrgsGate", params_dict, **_timeout_kwargs(timeout))) + + # Experimental: this API group is experimental and may change or be removed. class _InternalServerHostApi: def __init__(self, client: "JsonRpcClient"): @@ -55831,6 +61451,86 @@ async def _ready(self, params: HostReadyRequest, *, timeout: float | None = None return HostEmptyResult.from_dict(await self._client.request("host.ready", params_dict, **_timeout_kwargs(timeout))) +# Experimental: this API group is experimental and may change or be removed. +class _InternalServerAccountApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + + async def _describe_auth_info(self, params: _AccountDescribeAuthInfoRequest, *, timeout: float | None = None) -> _AccountDescribeAuthInfoResult: + "Renders the sign-in banner and the one-line account description the runtime uses for an authentication identity. The runtime words these in several places for its own callers, so a host that announces a sign-in or an account switch should render them from here rather than composing its own, which would let the two spellings drift apart.\n\nArgs:\n params: The authentication identity whose shared wording should be rendered.\n\nReturns:\n The wording the runtime uses for an authentication identity.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _AccountDescribeAuthInfoResult.from_dict(await self._client.request("account.describeAuthInfo", params_dict, **_timeout_kwargs(timeout))) + + async def _get_gist_support(self, params: _AccountGetGistSupportRequest, *, timeout: float | None = None) -> _AccountGetGistSupportResult: + "Reports whether the supplied identity can create gists. Gists are unavailable on GitHub Enterprise Cloud with data residency, and to Enterprise Managed Users on github.com, so a host that offers gist actions should hide or disable them when this reports no support.\n\nArgs:\n params: The authentication identity whose gist support should be reported.\n\nReturns:\n Whether the identity can create gists, and why not when it cannot.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _AccountGetGistSupportResult.from_dict(await self._client.request("account.getGistSupport", params_dict, **_timeout_kwargs(timeout))) + + async def _get_web_url(self, params: _AccountGetWebURLRequest, *, timeout: float | None = None) -> _AccountGetWebURLResult: + "Resolves a path on the identity's GitHub host into an absolute URL, so a host can link to settings or documentation on the enterprise instance the user is signed in to rather than to github.com. Falls back to the public host when no identity is supplied.\n\nArgs:\n params: A path to resolve against the GitHub host an identity is signed in to.\n\nReturns:\n An absolute URL on the identity's GitHub host.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _AccountGetWebURLResult.from_dict(await self._client.request("account.getWebUrl", params_dict, **_timeout_kwargs(timeout))) + + async def _get_token_env_var(self, *, timeout: float | None = None) -> _AccountGetTokenEnvVarResult: + "Names the environment variable supplying a classic personal access token, if one is set. A classic token cannot be upgraded in place, so a host that finds one should tell the user which variable to unset rather than starting an interactive sign-in that the variable would override.\n\nReturns:\n The environment variable supplying a classic personal access token, when one is set.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + return _AccountGetTokenEnvVarResult.from_dict(await self._client.request("account.getTokenEnvVar", {}, **_timeout_kwargs(timeout))) + + +# Experimental: this API group is experimental and may change or be removed. +class _InternalServerMcpRegistryApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + + async def _allocate_request_id(self, *, timeout: float | None = None) -> _MCPRegistryRequestIDResult: + "Allocates an ID for one cancellable MCP registry search. The ID exists before the search starts, so callers can cancel before it starts. The networking stack supplies the cancellation namespace. The runtime retains at most 1,024 unused IDs. At capacity, another allocation can reclaim an unused ID. Active searches retain their IDs until they finish.\n\nReturns:\n Request id naming a cancellable registry search.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + return _MCPRegistryRequestIDResult.from_dict(await self._client.request("mcp.registry.allocateRequestId", {}, **_timeout_kwargs(timeout))) + + async def _search(self, params: _MCPRegistrySearchRequest, *, timeout: float | None = None) -> _MCPRegistrySearchResult: + "Searches the MCP registry the supplied credential may read, resolving the registry endpoint from policy first. Hosts usually send credential-free `AuthIdentity`; a `token` identity can be resolved only when it embeds a token, while `env` and `gh-cli` identities can use a token embedded in the request first. The runtime follows registry pages, keeps the newest entry per server name, cuts the list to `limit`, and sorts an empty-query result by GitHub stars. Each server object is carried opaquely.\n\nArgs:\n params: Registry search terms, the credential to search under, and the request id that makes the search cancellable.\n\nReturns:\n Servers selected from the registry response.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _MCPRegistrySearchResult.from_dict(await self._client.request("mcp.registry.search", params_dict, **_timeout_kwargs(timeout))) + + async def _cancel(self, params: _MCPRegistryCancelRequest, *, timeout: float | None = None) -> _MCPRegistryCancelResult: + "Abandons the registry search that uses the given request ID. It acts only on IDs from `mcp.registry.allocateRequestId`, so it never cancels another component's request. Answers `canceled: true` when it stops a running search. Answers `canceled: false` for unknown or reclaimed IDs, completed or canceled searches, and unused reservations. It releases an unused reservation, so a later search with that ID is refused.\n\nArgs:\n params: Registry search to abandon.\n\nReturns:\n Whether the cancel reached a live search.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _MCPRegistryCancelResult.from_dict(await self._client.request("mcp.registry.cancel", params_dict, **_timeout_kwargs(timeout))) + + +# Experimental: this API group is experimental and may change or be removed. +class _InternalServerMcpApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + self.registry = _InternalServerMcpRegistryApi(client) + + async def _list_builtin_server_names(self, *, timeout: float | None = None) -> _MCPListBuiltinServerNamesResult: + "Lists the MCP servers the runtime supplies itself, so a host that disables the built-ins wholesale suppresses exactly the set the runtime would otherwise add. The names are fixed for a build and depend on no configuration, session or account.\n\nReturns:\n Names of the MCP servers the runtime supplies itself.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + return _MCPListBuiltinServerNamesResult.from_dict(await self._client.request("mcp.listBuiltinServerNames", {}, **_timeout_kwargs(timeout))) + + async def _should_exclude_git_hub_tools(self, params: _MCPShouldExcludeGitHubToolsRequest, *, timeout: float | None = None) -> _MCPShouldExcludeGitHubToolsResult: + "Reports whether the gh-replaceable GitHub MCP tools may be clipped for a session, which a host feeds back as the `excludeGhReplaceableTools` build option. The tools are redundant only when the session can reach a shell *and* the host has the `gh` that would replace them, so both halves are decided here. The shell half runs the platform shell tool through the runtime's own tool-filter matcher, so entry forms like `builtin:bash` and `builtin:*` behave exactly as they do when a session builds its tool catalog. The host half probes for `gh`, and is skipped entirely when the filters already rule the shell out. Host presence alone is not enough: a session restricted to, say, `view` would otherwise lose the built-in issue-read fallback while having no shell to replace it with. The answer describes the host the runtime runs on, so it is never forwarded to a remote engine.\n\nArgs:\n params: The session tool filters that decide whether the session still has a shell to run `gh` with.\n\nReturns:\n Whether the gh-replaceable GitHub MCP tools may be clipped for the session described by the request.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return _MCPShouldExcludeGitHubToolsResult.from_dict(await self._client.request("mcp.shouldExcludeGitHubTools", params_dict, **_timeout_kwargs(timeout))) + + +# Experimental: this API group is experimental and may change or be removed. +class _InternalServerInstructionsApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + + async def _get_init_prompt(self, *, timeout: float | None = None) -> _InstructionsInitPromptResult: + "Returns the prompt the `init` command runs to author repository instructions. A host offering its own initialize action sends this text so the instructions it produces match the ones the CLI produces. The text is fixed for a build and depends on no configuration, session or repository.\n\nReturns:\n The prompt the init command runs.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + return _InstructionsInitPromptResult.from_dict(await self._client.request("instructions.getInitPrompt", {}, **_timeout_kwargs(timeout))) + + async def _discover_repo_sources(self, params: _InstructionsDiscoverRepoSourcesRequest, *, timeout: float | None = None) -> ServerInstructionSourceList: + "Discovers the instruction sources for one caller-named location and returns them in the order the runtime applies them. `discover` answers for a set of project paths and derives the rest of its scope itself; this one instead takes every argument from the caller — the extra directories to scan and whether to descend into nested instruction files — and honors them as given. It also includes instruction directories from trusted repository settings, matching native repository discovery. Use it when the caller knows its own scope and no session exists to derive that scope from, such as a host reporting how many instruction files a directory contributes before any session starts. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract; it is migration plumbing, not a surface consumers are meant to depend on.\n\nArgs:\n params: Explicit discovery arguments for one location, supplied by the caller rather than derived from a session.\n\nReturns:\n Instruction sources discovered across user, repository, and plugin sources.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + 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.discoverRepoSources", params_dict, **_timeout_kwargs(timeout))) + + async def _clear_cache(self, *, timeout: float | None = None) -> None: + "Drops the runtime's in-memory cache of discovered custom instructions so the next discovery reads them from disk again. A host calls this after it changes instruction files, or after it trusts a folder whose instructions an earlier discovery skipped, so that a session started afterwards sees the current files instead of a cached earlier answer. The call performs no I/O of its own and removes nothing from disk; it only invalidates, so the cost lands on the next discovery. Returns once the cache is clear, which is what lets a caller order a session start after it. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract; it is migration plumbing, not a surface consumers are meant to depend on.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + await self._client.request("instructions.clearCache", {}, **_timeout_kwargs(timeout)) + + # Experimental: this API group is experimental and may change or be removed. class _InternalServerAgentsApi: def __init__(self, client: "JsonRpcClient"): @@ -55876,7 +61576,7 @@ async def _load_for_config_dir(self, params: _GlobalStateLoadForConfigDirRequest return _GlobalStateLoadResult.from_dict(await self._client.request("globalState.loadForConfigDir", params_dict, **_timeout_kwargs(timeout))) async def _write_key(self, params: _GlobalStateWriteKeyRequest, *, timeout: float | None = None) -> None: - "Records one top-level key in the host's machine-wide state, the counterpart to `globalState.load`. A host calls this to remember that it has shown an onboarding step, asked a one-off question, or completed a migration, so the next run can skip it. Only the named key is replaced and the rest of the document is preserved, which lets two writers record different flags without overwriting each other; passing no value removes the key instead. Only the keys a host records itself are writable: `appInstallNudgeResponded`, `appTipShown`, `askedSetupTerminals`, `autoFeedbackLastPromptedAt`, `firstLaunchAt`, `recentModelIds`, `sandboxCredentialProxyCaDeclined` and `sandboxOnboardingShown`. Every other key is refused, including `installedPlugins`, the stored credentials, `trustedFolders`, the staff flags and the signed-in accounts. Plugin enablement must use the plugin APIs, which apply repository and managed-policy checks.\n\nArgs:\n params: A single top-level key to record in the host's machine-wide state. The write replaces only that key and leaves the rest of the document untouched, so two writers recording different one-off flags do not overwrite each other. The stored credential keys cannot be written through this method.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + "Records one top-level key in the host's machine-wide state, the counterpart to `globalState.load`. A host calls this to remember that it has shown an onboarding step, asked a one-off question, or completed a migration, so the next run can skip it. Only the named key is replaced and the rest of the document is preserved, which lets two writers record different flags without overwriting each other; passing no value removes the key instead. Only the keys a host records itself are writable: `appTipShown`, `askedSetupTerminals`, `autoFeedbackLastPromptedAt`, `firstLaunchAt`, `recentModelIds`, `sandboxCredentialProxyCaDeclined` and `sandboxOnboardingShown`. Every other key is refused, including `installedPlugins`, the stored credentials, `trustedFolders`, the staff flags and the signed-in accounts. Plugin enablement must use the plugin APIs, which apply repository and managed-policy checks.\n\nArgs:\n params: A single top-level key to record in the host's machine-wide state. The write replaces only that key and leaves the rest of the document untouched, so two writers recording different one-off flags do not overwrite each other. The stored credential keys cannot be written through this method.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} await self._client.request("globalState.writeKey", params_dict, **_timeout_kwargs(timeout)) @@ -55897,9 +61597,9 @@ class _InternalServerGitHubOwnersApi: def __init__(self, client: "JsonRpcClient"): self._client = client - async def _next_request_id(self, *, timeout: float | None = None) -> GitHubOwnersRequestIDResult: + async def _next_request_id(self, *, timeout: float | None = None) -> _GitHubOwnersRequestIDResult: "Registers a cancellable owner listing and returns its request id. Separate from `gitHubOwners.list` so the id exists before the listing starts: a caller that abandons the listing the moment it begins would otherwise have nothing to name in `gitHubOwners.cancel`. The id serves one listing only. Long-abandoned unused ids can be released by later allocations.\n\nReturns:\n A freshly registered request id. Registering it before the listing starts is what lets a cancel that races the request still find the owner listing slot. The id serves one listing only. Long-abandoned unused ids can be released by later allocations.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." - return GitHubOwnersRequestIDResult.from_dict(await self._client.request("gitHubOwners.nextRequestId", {}, **_timeout_kwargs(timeout))) + return _GitHubOwnersRequestIDResult.from_dict(await self._client.request("gitHubOwners.nextRequestId", {}, **_timeout_kwargs(timeout))) async def _list(self, params: _GitHubOwnersListRequest, *, timeout: float | None = None) -> _GitHubOwnersListResult: "Lists the logins the authenticated user may act as — their own account first, then the organizations they belong to — by asking the GitHub API under the supplied credential. No credential travels in the request: `authInfo` selects one the runtime already holds, and the runtime resolves the token and the GitHub host from it. A failure the caller should render arrives as `message`; one it should raise arrives as `throwError`.\n\nArgs:\n params: Credential to list owners under, and the request id that makes the listing cancellable.\n\nReturns:\n Outcome of an owner listing. Exactly one of `owners` and `message` is present, except that `throwError` reports a failure the caller is expected to raise rather than render.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." @@ -55993,7 +61693,17 @@ class _InternalServerRpc: """Internal SDK server-scoped RPC methods. Not part of the public API.""" def __init__(self, client: "JsonRpcClient"): self._client = client + self.sandbox = _InternalServerSandboxApi(client) + self.persistence = _InternalServerPersistenceApi(client) + self.user = _InternalServerUserApi(client) + self.repo = _InternalServerRepoApi(client) + self.feature_flags = _InternalServerFeatureFlagsApi(client) + self.permissions = _InternalServerPermissionsApi(client) + self.managed_settings = _InternalServerManagedSettingsApi(client) self.host = _InternalServerHostApi(client) + self.account = _InternalServerAccountApi(client) + self.mcp = _InternalServerMcpApi(client) + self.instructions = _InternalServerInstructionsApi(client) self.agents = _InternalServerAgentsApi(client) self.global_state = _InternalServerGlobalStateApi(client) self.git_hub_repository = _InternalServerGitHubRepositoryApi(client) @@ -56060,6 +61770,10 @@ async def get_enforcement_status(self, *, timeout: float | None = None) -> Sandb "Returns whether managed policy requires sandbox enforcement and whether an enforcement failure has permanently blocked the session.\n\nReturns:\n Managed sandbox enforcement state for a session." return SandboxEnforcementStatus.from_dict(await self._client.request("session.sandbox.getEnforcementStatus", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + async def get_credential_suggestions(self, *, timeout: float | None = None) -> SandboxCredentialSuggestionsResult: + "Lists possible secret-bearing environment variables available to new sandboxed shells that are not already covered by credential masking. Uses local name rules and known-provider host suggestions, never returns secret values, and changes no settings or network permissions. Returns no suggestions while the session is not sandboxed.\n\nReturns:\n Possible unconfigured secrets in the sandbox shell environment, sorted by variable name, with no secret values." + return SandboxCredentialSuggestionsResult.from_dict(await self._client.request("session.sandbox.getCredentialSuggestions", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + async def disable_for_session(self, params: SandboxDisableForSessionRequest, *, timeout: float | None = None) -> SandboxDisableForSessionResult: "Disables sandboxing for the remainder of the current session and approves the referenced pending sandbox-bypass permission request. The request is rejected unless the exact request is still pending and the effective sandbox policy permits bypass.\n\nArgs:\n params: Request to disable sandboxing for the current session while resolving an active sandbox-bypass permission prompt.\n\nReturns:\n Result of attempting to disable sandboxing for the current session." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -56316,6 +62030,12 @@ async def set_reasoning_effort(self, params: ModelSetReasoningEffortRequest, *, params_dict["sessionId"] = self._session_id return ModelSetReasoningEffortResult.from_dict(await self._client.request("session.model.setReasoningEffort", params_dict, **_timeout_kwargs(timeout))) + async def get_reasoning_summaries(self, params: ModelGetReasoningSummariesRequest, *, timeout: float | None = None) -> ModelGetReasoningSummariesResult: + "Computes whether the session's current model shows reasoning summaries and how a host should present them: the default Ctrl+T visibility, reasoning headers, the hidden-summaries hint, and whether a persisted `showReasoning` setting is redundant. The runtime folds in the session's model catalog, selection and reasoning-summaries experiment assignment; the host supplies only its own persisted setting and current visibility. This method reads the session's resolved model catalog rather than resolving it, so call `session.model.list` first on a session that has not listed models or run a turn; until then the result is `ready: false`. A session that resolves models without a Copilot catalog, that is a BYOK registry or an alternate provider, never caches one and stays `ready: false`.\n\nArgs:\n params: Host presentation state the reasoning-summaries policy depends on.\n\nReturns:\n Reasoning-summaries presentation policy for the session's current model." + 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 ModelGetReasoningSummariesResult.from_dict(await self._client.request("session.model.getReasoningSummaries", params_dict, **_timeout_kwargs(timeout))) + async def list(self, params: SessionModelListRequest | 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 {} @@ -57140,6 +62860,12 @@ async def reload(self, params: SessionPluginsReloadRequest | None = None, *, tim params_dict["sessionId"] = self._session_id await self._client.request("session.plugins.reload", params_dict, **_timeout_kwargs(timeout)) + async def retry_managed(self, params: SessionPluginsRetryManagedRequest | None = None, *, timeout: float | None = None) -> SessionPluginsRetryManagedResult: + "Installs or updates the plugins the session's managed settings require now, without waiting for the next automatic attempt after a failure, then reloads plugins. Reports progress and failures with the same `session.info` and `session.warning` events as automatic preparation. Fails when the runtime does not prepare managed plugins for the session.\n\nArgs:\n params: Plugins required by the session's managed settings to install or update again.\n\nReturns:\n Per-plugin outcomes of retrying the plugins required by the session's managed settings." + 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 SessionPluginsRetryManagedResult.from_dict(await self._client.request("session.plugins.retryManaged", params_dict, **_timeout_kwargs(timeout))) + # Experimental: this API group is experimental and may change or be removed. class ProviderApi: @@ -58163,25 +63889,25 @@ async def _set_connected_ide_info(self, params: SessionMCPSetConnectedIDEInfoPar params_dict["sessionId"] = self._session_id await self._client.request("session.mcp.setConnectedIdeInfo", params_dict, **_timeout_kwargs(timeout)) - async def _reload_with_config(self, params: MCPReloadWithConfigRequest, *, timeout: float | None = None) -> MCPStartServersResult: + async def _reload_with_config(self, params: _MCPReloadWithConfigRequest, *, timeout: float | None = None) -> MCPStartServersResult: "Reloads MCP server connections for the session with an explicit host-provided configuration.\n\nArgs:\n params: Opaque MCP reload configuration.\n\nReturns:\n MCP server startup filtering result.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." 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 MCPStartServersResult.from_dict(await self._client.request("session.mcp.reloadWithConfig", params_dict, **_timeout_kwargs(timeout))) - async def _configure_git_hub(self, params: MCPConfigureGitHubRequest, *, timeout: float | None = None) -> MCPConfigureGitHubResult: + async def _configure_git_hub(self, params: _MCPConfigureGitHubRequest, *, timeout: float | None = None) -> MCPConfigureGitHubResult: "Configures the built-in GitHub MCP server for the session's current auth context.\n\nArgs:\n params: Credential-free authentication identity used to configure GitHub MCP.\n\nReturns:\n Result of configuring GitHub MCP.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." 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 MCPConfigureGitHubResult.from_dict(await self._client.request("session.mcp.configureGitHub", params_dict, **_timeout_kwargs(timeout))) - async def _register_external_client(self, params: MCPRegisterExternalClientRequest, *, timeout: float | None = None) -> None: + async def _register_external_client(self, params: _MCPRegisterExternalClientRequest, *, timeout: float | None = None) -> None: "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.\n\nArgs:\n params: Registration parameters for an external MCP client.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." 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.registerExternalClient", params_dict, **_timeout_kwargs(timeout)) - async def _unregister_external_client(self, params: MCPUnregisterExternalClientRequest, *, timeout: float | None = None) -> None: + async def _unregister_external_client(self, params: _MCPUnregisterExternalClientRequest, *, timeout: float | None = None) -> None: "Unregisters a previously registered external MCP client by server name. Marked internal as the paired companion of `registerExternalClient`: only in-process callers that registered a client this way can meaningfully unregister it. Disappears alongside `registerExternalClient`: once external clients are described to the runtime as config rather than handed in as instances, lifecycle (including deregistration) is owned entirely by the runtime.\n\nArgs:\n params: Server name identifying the external client to remove.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." 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 @@ -59235,6 +64961,8 @@ async def handle_installations_confirm(params: dict) -> dict | None: "ExternalToolTextResultForLlmContentTerminalType", "ExternalToolTextResultForLlmContentText", "ExternalToolTextResultForLlmContentType", + "FeatureFlagsResolveResult", + "FeatureFlagsResolveStartupGatesResult", "FilterMapping", "FleetApi", "FleetStartRequest", @@ -59247,7 +64975,6 @@ async def handle_installations_confirm(params: dict) -> dict | None: "GhCLIAuthInfoType", "GitHubAuthApi", "GitHubEnvironment", - "GitHubOwnersRequestIDResult", "GitHubRepositoryIdentity", "GitHubTelemetryClientInfo", "GitHubTelemetryEvent", @@ -59395,7 +65122,6 @@ async def handle_installations_confirm(params: dict) -> dict | None: "MCPConfigList", "MCPConfigRemoveRequest", "MCPConfigUpdateRequest", - "MCPConfigureGitHubRequest", "MCPConfigureGitHubResult", "MCPConfiguredServer", "MCPConfiguredServerList", @@ -59512,9 +65238,6 @@ async def handle_installations_confirm(params: dict) -> dict | None: "MCPPromptsGetResult", "MCPPromptsListRequest", "MCPPromptsListResult", - "MCPRegisterExternalClientRequest", - "MCPReloadConfig", - "MCPReloadWithConfigRequest", "MCPRemoveGitHubResult", "MCPResource", "MCPResourceAnnotations", @@ -59545,8 +65268,6 @@ async def handle_installations_confirm(params: dict) -> dict | None: "MCPServerConfigDeferTools", "MCPServerConfigHTTP", "MCPServerConfigHTTPType", - "MCPServerConfigMemory", - "MCPServerConfigMemoryType", "MCPServerConfigStdio", "MCPServerConfigStdioType", "MCPServerConfigType", @@ -59568,8 +65289,15 @@ async def handle_installations_confirm(params: dict) -> dict | None: "MCPToolUIVisibility", "MCPTools", "MCPUninstallPlan", - "MCPUnregisterExternalClientRequest", "ManagedMCPServerConfig", + "ManagedPermissionEvaluation", + "ManagedPermissionOperation", + "ManagedPermissionOperationKind", + "ManagedPermissionVerdict", + "ManagedPermissionsEvaluateRequest", + "ManagedPermissionsEvaluateResult", + "ManagedPluginRetryEntry", + "ManagedPluginRetryStatus", "ManagedSettingMeta", "ManagedSettingsApi", "ManagedSettingsChannel", @@ -59662,6 +65390,8 @@ async def handle_installations_confirm(params: dict) -> dict | None: "ModelCapabilitiesOverrideLimitsVision", "ModelCapabilitiesOverrideSupports", "ModelCapabilitiesSupports", + "ModelGetReasoningSummariesRequest", + "ModelGetReasoningSummariesResult", "ModelList", "ModelListRequest", "ModelMessage", @@ -59829,6 +65559,7 @@ async def handle_installations_confirm(params: dict) -> dict | None: "PermissionsConfigureAdditionalContentExclusionPolicyScope", "PermissionsConfigureParams", "PermissionsConfigureResult", + "PermissionsEvaluateManagedPolicyResult", "PermissionsFolderTrustAddTrustedResult", "PermissionsFolderTrustApi", "PermissionsGetModeRequest", @@ -59867,6 +65598,8 @@ async def handle_installations_confirm(params: dict) -> dict | None: "PermissionsSetRequiredResult", "PermissionsUrlsApi", "PermissionsUrlsSetUnrestrictedModeResult", + "PersistenceWriteRawJSONLockedRequest", + "PersistenceWriteRawJSONLockedResult", "Phase", "PingRequest", "PingResult", @@ -60054,6 +65787,10 @@ async def handle_installations_confirm(params: dict) -> dict | None: "ResponseFormatType", "ReviewClass", "SandboxApi", + "SandboxApplyDefaultsRequest", + "SandboxCheckEnforcementCommandResult", + "SandboxCheckPlatformSupportResult", + "SandboxCheckWindowsHostLoopbackSupportResult", "SandboxConfig", "SandboxConfigAuth", "SandboxConfigSource", @@ -60064,10 +65801,15 @@ async def handle_installations_confirm(params: dict) -> dict | None: "SandboxConfigUserPolicyNetwork", "SandboxConfigUserPolicyNetworkProxy", "SandboxConfigUserPolicySeatbelt", + "SandboxCredentialSuggestion", + "SandboxCredentialSuggestionsResult", "SandboxCredentialsConfig", "SandboxDisableForSessionRequest", "SandboxDisableForSessionResult", + "SandboxDocsURLResult", "SandboxEnforcementStatus", + "SandboxGetUnsupportedMessageResult", + "SandboxGetUnsupportedSettingDisabledNoticeResult", "SandboxGrantPathForRequestRequest", "SandboxGrantPathForRequestResult", "SandboxHostCapability", @@ -60078,7 +65820,15 @@ async def handle_installations_confirm(params: dict) -> dict | None: "SandboxProxyCARequest", "SandboxProxyCAState", "SandboxProxyCAStatus", + "SandboxProxyCaCheckInstallationSupportResult", + "SandboxProxyCaCheckRelevanceResult", + "SandboxProxySecretGetResult", + "SandboxProxySecretLocation", + "SandboxProxySecretSaveRequest", + "SandboxProxySecretSaveResult", + "SandboxResolveFlooredConfigResult", "SandboxSessionChange", + "SandboxSettingsApplyDefaultsResult", "Saved", "ScheduleAddAtRequest", "ScheduleAddCronRequest", @@ -60118,6 +65868,7 @@ async def handle_installations_confirm(params: dict) -> dict | None: "ServerInstructionsApi", "ServerLlmInferenceApi", "ServerManagedSettingsApi", + "ServerManagedSettingsPermissionsApi", "ServerMcpApi", "ServerMcpConfigApi", "ServerMcpInstallationsApi", @@ -60249,6 +66000,8 @@ async def handle_installations_confirm(params: dict) -> dict | None: "SessionPluginsInstallRequest", "SessionPluginsMarketplacesRefreshRequest", "SessionPluginsReloadRequest", + "SessionPluginsRetryManagedRequest", + "SessionPluginsRetryManagedResult", "SessionProviderGetEndpointRequest", "SessionPruneResult", "SessionQuotaDelegateWarning", @@ -60437,6 +66190,8 @@ async def handle_installations_confirm(params: dict) -> dict | None: "SlashCommandTextResult", "SlashCommandTimelineEntry", "SourceClass", + "StaffLogLevelMigrationStatus", + "StaffUpdateChannelMigrationStatus", "SubagentSettings", "SubagentSettingsEntry", "SubagentSettingsEntryContextTier", @@ -60561,6 +66316,7 @@ async def handle_installations_confirm(params: dict) -> dict | None: "UIUnregisterDirectAutoModeSwitchHandlerRequest", "UIUnregisterDirectAutoModeSwitchHandlerResult", "UIUserInputResponse", + "UNSET_SENTINEL", "UiApi", "UnsupportedEnqueueCommandResult", "UpdateSubagentSettingsRequest", @@ -60568,8 +66324,34 @@ async def handle_installations_confirm(params: dict) -> dict | None: "UserAuthInfo", "UserRequestedShellCommandResult", "UserSettingMetadata", + "UserSettingsApplyTopKeyRequest", + "UserSettingsApplyTopKeyResult", + "UserSettingsBuildRuntimeResult", + "UserSettingsGetPathsRequest", + "UserSettingsGetPathsResult", "UserSettingsGetResult", + "UserSettingsGovernanceKeysResult", + "UserSettingsHostContext", + "UserSettingsLoadForConfigDirResult", + "UserSettingsLoadForEditResult", + "UserSettingsLoadRepoRequest", + "UserSettingsLoadRepoResult", + "UserSettingsLoadWithWarningResult", + "UserSettingsMergeManagedAndUserResult", + "UserSettingsMergeRepoValueResult", + "UserSettingsNormalizeRequest", + "UserSettingsNormalizeResult", + "UserSettingsPathResult", + "UserSettingsProblem", + "UserSettingsProblemLocation", + "UserSettingsProblemSource", + "UserSettingsProblemsRequest", + "UserSettingsProblemsResult", "UserSettingsSetRequest", + "UserSettingsValidateRequest", + "UserSettingsValidateResult", + "UserSettingsValidationIssue", + "UserSettingsWriteAllRequest", "VisibilityApi", "VisibilityGetResult", "VisibilitySetRequest", diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py index 8de79749b4..9291f489a5 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -1788,6 +1788,54 @@ def to_dict(self) -> dict: return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class ProviderMonthlyUsage: + "An authoritative monthly usage reading for the observation's provider, account, and service." + scope: ProviderMonthlyUsageScope + state: ProviderMonthlyUsageState + unit: ProviderQuotaUnit + consumed_quantity: float | None = None + cycle_start: str | None = None + queried_at: str | None = None + reset_on: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "ProviderMonthlyUsage": + assert isinstance(obj, dict) + scope = parse_enum(ProviderMonthlyUsageScope, obj.get("scope")) + state = parse_enum(ProviderMonthlyUsageState, obj.get("state")) + unit = parse_enum(ProviderQuotaUnit, obj.get("unit")) + consumed_quantity = from_union([from_none, from_float], obj.get("consumedQuantity")) + cycle_start = from_union([from_none, from_str], obj.get("cycleStart")) + queried_at = from_union([from_none, from_str], obj.get("queriedAt")) + reset_on = from_union([from_none, from_str], obj.get("resetOn")) + return ProviderMonthlyUsage( + scope=scope, + state=state, + unit=unit, + consumed_quantity=consumed_quantity, + cycle_start=cycle_start, + queried_at=queried_at, + reset_on=reset_on, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["scope"] = to_enum(ProviderMonthlyUsageScope, self.scope) + result["state"] = to_enum(ProviderMonthlyUsageState, self.state) + result["unit"] = to_enum(ProviderQuotaUnit, self.unit) + if self.consumed_quantity is not None: + result["consumedQuantity"] = from_union([from_none, to_float], self.consumed_quantity) + if self.cycle_start is not None: + result["cycleStart"] = from_union([from_none, from_str], self.cycle_start) + if self.queried_at is not None: + result["queriedAt"] = from_union([from_none, from_str], self.queried_at) + if self.reset_on is not None: + result["resetOn"] = from_union([from_none, from_str], self.reset_on) + return result + + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ProviderQuotaState: @@ -1805,6 +1853,7 @@ class ProviderQuotaState: entitled_quantity: int | None = None has_quota: bool | None = None http_status: int | None = None + monthly_usage: ProviderMonthlyUsage | None = None observation_kind: ProviderQuotaObservationKind | None = None observed_at: str | None = None quantity_kind: ProviderQuotaQuantityKind | None = None @@ -1828,6 +1877,7 @@ def from_dict(obj: Any) -> "ProviderQuotaState": entitled_quantity = from_union([from_none, from_int], obj.get("entitledQuantity")) has_quota = from_union([from_none, from_bool], obj.get("hasQuota")) http_status = from_union([from_none, from_int], obj.get("httpStatus")) + monthly_usage = from_union([from_none, ProviderMonthlyUsage.from_dict], obj.get("monthlyUsage")) observation_kind = from_union([from_none, lambda x: parse_enum(ProviderQuotaObservationKind, x)], obj.get("observationKind")) observed_at = from_union([from_none, from_str], obj.get("observedAt")) quantity_kind = from_union([from_none, lambda x: parse_enum(ProviderQuotaQuantityKind, x)], obj.get("quantityKind")) @@ -1848,6 +1898,7 @@ def from_dict(obj: Any) -> "ProviderQuotaState": entitled_quantity=entitled_quantity, has_quota=has_quota, http_status=http_status, + monthly_usage=monthly_usage, observation_kind=observation_kind, observed_at=observed_at, quantity_kind=quantity_kind, @@ -1879,6 +1930,8 @@ def to_dict(self) -> dict: result["hasQuota"] = from_union([from_none, from_bool], self.has_quota) if self.http_status is not None: result["httpStatus"] = from_union([from_none, to_int], self.http_status) + if self.monthly_usage is not None: + result["monthlyUsage"] = from_union([from_none, lambda x: to_class(ProviderMonthlyUsage, x)], self.monthly_usage) if self.observation_kind is not None: result["observationKind"] = from_union([from_none, lambda x: to_enum(ProviderQuotaObservationKind, x)], self.observation_kind) if self.observed_at is not None: @@ -2636,6 +2689,7 @@ class SessionManagedSettingsResolvedData: source: ManagedSettingsResolvedSource client_managed: bool | None = None permissions_allow_intersected: bool | None = None + permissions_context: ManagedPermissionsContext | None = None policy_helper_managed: bool | None = None sandbox_enabled_by_undetermined_policy: bool | None = None settings: Any = None @@ -2651,6 +2705,7 @@ def from_dict(obj: Any) -> "SessionManagedSettingsResolvedData": source = parse_enum(ManagedSettingsResolvedSource, obj.get("source")) client_managed = from_union([from_none, from_bool], obj.get("clientManaged")) permissions_allow_intersected = from_union([from_none, from_bool], obj.get("permissionsAllowIntersected")) + permissions_context = from_union([from_none, ManagedPermissionsContext.from_dict], obj.get("permissionsContext")) policy_helper_managed = from_union([from_none, from_bool], obj.get("policyHelperManaged")) sandbox_enabled_by_undetermined_policy = from_union([from_none, from_bool], obj.get("sandboxEnabledByUndeterminedPolicy")) settings = obj.get("settings") @@ -2663,6 +2718,7 @@ def from_dict(obj: Any) -> "SessionManagedSettingsResolvedData": source=source, client_managed=client_managed, permissions_allow_intersected=permissions_allow_intersected, + permissions_context=permissions_context, policy_helper_managed=policy_helper_managed, sandbox_enabled_by_undetermined_policy=sandbox_enabled_by_undetermined_policy, settings=settings, @@ -2680,6 +2736,8 @@ def to_dict(self) -> dict: result["clientManaged"] = from_union([from_none, from_bool], self.client_managed) if self.permissions_allow_intersected is not None: result["permissionsAllowIntersected"] = from_union([from_none, from_bool], self.permissions_allow_intersected) + if self.permissions_context is not None: + result["permissionsContext"] = from_union([from_none, lambda x: to_class(ManagedPermissionsContext, x)], self.permissions_context) if self.policy_helper_managed is not None: result["policyHelperManaged"] = from_union([from_none, from_bool], self.policy_helper_managed) if self.sandbox_enabled_by_undetermined_policy is not None: @@ -3708,6 +3766,8 @@ class AssistantUsageData: reasoning_tokens: int | None = None rejected_prediction_tokens: int | None = None request_body_bytes: int | None = None + request_body_encoding: ModelCallRequestBodyEncoding | None = None + request_body_wire_bytes: int | None = None rte: bool | None = None service_request_id: str | None = None # Internal: this field is an internal SDK API and is not part of the public surface. @@ -3721,7 +3781,12 @@ class AssistantUsageData: _tool_token_count: int | None = None transport: AssistantUsageTransport | None = None websocket_fallback_after: timedelta | None = None + websocket_fallback_close_code: int | None = None + websocket_fallback_error_kind: ModelCallWebSocketFallbackErrorKind | None = None websocket_fallback_reason: ModelCallWebSocketFallbackReason | None = None + websocket_fallback_reconnect_attempted: bool | None = None + websocket_fallback_started_this_call: bool | None = None + websocket_fallback_status_code: int | None = None @staticmethod def from_dict(obj: Any) -> "AssistantUsageData": @@ -3768,6 +3833,8 @@ def from_dict(obj: Any) -> "AssistantUsageData": reasoning_tokens = from_union([from_none, from_int], obj.get("reasoningTokens")) rejected_prediction_tokens = from_union([from_none, from_int], obj.get("rejectedPredictionTokens")) request_body_bytes = from_union([from_none, from_int], obj.get("requestBodyBytes")) + request_body_encoding = from_union([from_none, lambda x: parse_enum(ModelCallRequestBodyEncoding, x)], obj.get("requestBodyEncoding")) + request_body_wire_bytes = from_union([from_none, from_int], obj.get("requestBodyWireBytes")) rte = from_union([from_none, from_bool], obj.get("rte")) service_request_id = from_union([from_none, from_str], obj.get("serviceRequestId")) _thinking_dropped_blocks = from_union([from_none, from_int], obj.get("thinkingDroppedBlocks")) @@ -3777,7 +3844,12 @@ def from_dict(obj: Any) -> "AssistantUsageData": _tool_token_count = from_union([from_none, from_int], obj.get("toolTokenCount")) transport = from_union([from_none, lambda x: parse_enum(AssistantUsageTransport, x)], obj.get("transport")) websocket_fallback_after = from_union([from_none, from_timedelta], obj.get("websocketFallbackAfterMs")) + websocket_fallback_close_code = from_union([from_none, from_int], obj.get("websocketFallbackCloseCode")) + websocket_fallback_error_kind = from_union([from_none, lambda x: parse_enum(ModelCallWebSocketFallbackErrorKind, x)], obj.get("websocketFallbackErrorKind")) websocket_fallback_reason = from_union([from_none, lambda x: parse_enum(ModelCallWebSocketFallbackReason, x)], obj.get("websocketFallbackReason")) + websocket_fallback_reconnect_attempted = from_union([from_none, from_bool], obj.get("websocketFallbackReconnectAttempted")) + websocket_fallback_started_this_call = from_union([from_none, from_bool], obj.get("websocketFallbackStartedThisCall")) + websocket_fallback_status_code = from_union([from_none, from_int], obj.get("websocketFallbackStatusCode")) return AssistantUsageData( model=model, accepted_prediction_tokens=accepted_prediction_tokens, @@ -3821,6 +3893,8 @@ def from_dict(obj: Any) -> "AssistantUsageData": reasoning_tokens=reasoning_tokens, rejected_prediction_tokens=rejected_prediction_tokens, request_body_bytes=request_body_bytes, + request_body_encoding=request_body_encoding, + request_body_wire_bytes=request_body_wire_bytes, rte=rte, service_request_id=service_request_id, _thinking_dropped_blocks=_thinking_dropped_blocks, @@ -3830,7 +3904,12 @@ def from_dict(obj: Any) -> "AssistantUsageData": _tool_token_count=_tool_token_count, transport=transport, websocket_fallback_after=websocket_fallback_after, + websocket_fallback_close_code=websocket_fallback_close_code, + websocket_fallback_error_kind=websocket_fallback_error_kind, websocket_fallback_reason=websocket_fallback_reason, + websocket_fallback_reconnect_attempted=websocket_fallback_reconnect_attempted, + websocket_fallback_started_this_call=websocket_fallback_started_this_call, + websocket_fallback_status_code=websocket_fallback_status_code, ) def to_dict(self) -> dict: @@ -3918,6 +3997,10 @@ def to_dict(self) -> dict: result["rejectedPredictionTokens"] = from_union([from_none, to_int], self.rejected_prediction_tokens) if self.request_body_bytes is not None: result["requestBodyBytes"] = from_union([from_none, to_int], self.request_body_bytes) + if self.request_body_encoding is not None: + result["requestBodyEncoding"] = from_union([from_none, lambda x: to_enum(ModelCallRequestBodyEncoding, x)], self.request_body_encoding) + if self.request_body_wire_bytes is not None: + result["requestBodyWireBytes"] = from_union([from_none, to_int], self.request_body_wire_bytes) if self.rte is not None: result["rte"] = from_union([from_none, from_bool], self.rte) if self.service_request_id is not None: @@ -3936,8 +4019,18 @@ def to_dict(self) -> dict: result["transport"] = from_union([from_none, lambda x: to_enum(AssistantUsageTransport, x)], self.transport) if self.websocket_fallback_after is not None: result["websocketFallbackAfterMs"] = from_union([from_none, to_timedelta_int], self.websocket_fallback_after) + if self.websocket_fallback_close_code is not None: + result["websocketFallbackCloseCode"] = from_union([from_none, to_int], self.websocket_fallback_close_code) + if self.websocket_fallback_error_kind is not None: + result["websocketFallbackErrorKind"] = from_union([from_none, lambda x: to_enum(ModelCallWebSocketFallbackErrorKind, x)], self.websocket_fallback_error_kind) if self.websocket_fallback_reason is not None: result["websocketFallbackReason"] = from_union([from_none, lambda x: to_enum(ModelCallWebSocketFallbackReason, x)], self.websocket_fallback_reason) + if self.websocket_fallback_reconnect_attempted is not None: + result["websocketFallbackReconnectAttempted"] = from_union([from_none, from_bool], self.websocket_fallback_reconnect_attempted) + if self.websocket_fallback_started_this_call is not None: + result["websocketFallbackStartedThisCall"] = from_union([from_none, from_bool], self.websocket_fallback_started_this_call) + if self.websocket_fallback_status_code is not None: + result["websocketFallbackStatusCode"] = from_union([from_none, to_int], self.websocket_fallback_status_code) return result @@ -4928,6 +5021,7 @@ class CompactionCompleteCompactionTokensUsed: model_display_name: str | None = None output_tokens: int | None = None provider: ModelProviderRef | None = None + request_count: int | None = None @staticmethod def from_dict(obj: Any) -> "CompactionCompleteCompactionTokensUsed": @@ -4942,6 +5036,7 @@ def from_dict(obj: Any) -> "CompactionCompleteCompactionTokensUsed": model_display_name = from_union([from_none, from_str], obj.get("modelDisplayName")) output_tokens = from_union([from_none, from_int], obj.get("outputTokens")) provider = from_union([from_none, ModelProviderRef.from_dict], obj.get("provider")) + request_count = from_union([from_none, from_int], obj.get("requestCount")) return CompactionCompleteCompactionTokensUsed( ai_credits_status=ai_credits_status, cache_read_tokens=cache_read_tokens, @@ -4953,6 +5048,7 @@ def from_dict(obj: Any) -> "CompactionCompleteCompactionTokensUsed": model_display_name=model_display_name, output_tokens=output_tokens, provider=provider, + request_count=request_count, ) def to_dict(self) -> dict: @@ -4977,6 +5073,8 @@ def to_dict(self) -> dict: result["outputTokens"] = from_union([from_none, to_int], self.output_tokens) if self.provider is not None: result["provider"] = from_union([from_none, lambda x: to_class(ModelProviderRef, x)], self.provider) + if self.request_count is not None: + result["requestCount"] = from_union([from_none, to_int], self.request_count) return result @@ -5885,6 +5983,30 @@ def to_dict(self) -> dict: return result +@dataclass +class ManagedPermissionsContext: + "Reusable managed permission evaluation context. Treat permissions as runtime-owned policy data and retain it verbatim; it includes source-aware composition metadata. This is a snapshot, not a capability or approval token; only use contexts obtained from trusted policy sources." + fail_closed: bool + permissions: Any = None + + @staticmethod + def from_dict(obj: Any) -> "ManagedPermissionsContext": + assert isinstance(obj, dict) + fail_closed = from_bool(obj.get("failClosed")) + permissions = obj.get("permissions") + return ManagedPermissionsContext( + fail_closed=fail_closed, + permissions=permissions, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["failClosed"] = from_bool(self.fail_closed) + if self.permissions is not None: + result["permissions"] = self.permissions + return result + + @dataclass class McpAppToolCallCompleteData: "MCP App view called a tool on a connected MCP server (SEP-1865)" @@ -6388,6 +6510,8 @@ class ModelCallFailureData: _quota_snapshots: dict[str, _AssistantUsageQuotaSnapshot] | None = None reasoning_effort: str | None = None request_body_bytes: int | None = None + request_body_encoding: ModelCallRequestBodyEncoding | None = None + request_body_wire_bytes: int | None = None request_fingerprint: ModelCallFailureRequestFingerprint | None = None retry_attempt: int | None = None rte: bool | None = None @@ -6395,7 +6519,12 @@ class ModelCallFailureData: status_code: int | None = None transport: ModelCallFailureTransport | None = None websocket_fallback_after: timedelta | None = None + websocket_fallback_close_code: int | None = None + websocket_fallback_error_kind: ModelCallWebSocketFallbackErrorKind | None = None websocket_fallback_reason: ModelCallWebSocketFallbackReason | None = None + websocket_fallback_reconnect_attempted: bool | None = None + websocket_fallback_started_this_call: bool | None = None + websocket_fallback_status_code: int | None = None @staticmethod def from_dict(obj: Any) -> "ModelCallFailureData": @@ -6424,6 +6553,8 @@ def from_dict(obj: Any) -> "ModelCallFailureData": _quota_snapshots = from_union([from_none, lambda x: from_dict(_AssistantUsageQuotaSnapshot.from_dict, x)], obj.get("quotaSnapshots")) reasoning_effort = from_union([from_none, from_str], obj.get("reasoningEffort")) request_body_bytes = from_union([from_none, from_int], obj.get("requestBodyBytes")) + request_body_encoding = from_union([from_none, lambda x: parse_enum(ModelCallRequestBodyEncoding, x)], obj.get("requestBodyEncoding")) + request_body_wire_bytes = from_union([from_none, from_int], obj.get("requestBodyWireBytes")) request_fingerprint = from_union([from_none, ModelCallFailureRequestFingerprint.from_dict], obj.get("requestFingerprint")) retry_attempt = from_union([from_none, from_int], obj.get("retryAttempt")) rte = from_union([from_none, from_bool], obj.get("rte")) @@ -6431,7 +6562,12 @@ def from_dict(obj: Any) -> "ModelCallFailureData": status_code = from_union([from_none, from_int], obj.get("statusCode")) transport = from_union([from_none, lambda x: parse_enum(ModelCallFailureTransport, x)], obj.get("transport")) websocket_fallback_after = from_union([from_none, from_timedelta], obj.get("websocketFallbackAfterMs")) + websocket_fallback_close_code = from_union([from_none, from_int], obj.get("websocketFallbackCloseCode")) + websocket_fallback_error_kind = from_union([from_none, lambda x: parse_enum(ModelCallWebSocketFallbackErrorKind, x)], obj.get("websocketFallbackErrorKind")) websocket_fallback_reason = from_union([from_none, lambda x: parse_enum(ModelCallWebSocketFallbackReason, x)], obj.get("websocketFallbackReason")) + websocket_fallback_reconnect_attempted = from_union([from_none, from_bool], obj.get("websocketFallbackReconnectAttempted")) + websocket_fallback_started_this_call = from_union([from_none, from_bool], obj.get("websocketFallbackStartedThisCall")) + websocket_fallback_status_code = from_union([from_none, from_int], obj.get("websocketFallbackStatusCode")) return ModelCallFailureData( source=source, api_call_id=api_call_id, @@ -6457,6 +6593,8 @@ def from_dict(obj: Any) -> "ModelCallFailureData": _quota_snapshots=_quota_snapshots, reasoning_effort=reasoning_effort, request_body_bytes=request_body_bytes, + request_body_encoding=request_body_encoding, + request_body_wire_bytes=request_body_wire_bytes, request_fingerprint=request_fingerprint, retry_attempt=retry_attempt, rte=rte, @@ -6464,7 +6602,12 @@ def from_dict(obj: Any) -> "ModelCallFailureData": status_code=status_code, transport=transport, websocket_fallback_after=websocket_fallback_after, + websocket_fallback_close_code=websocket_fallback_close_code, + websocket_fallback_error_kind=websocket_fallback_error_kind, websocket_fallback_reason=websocket_fallback_reason, + websocket_fallback_reconnect_attempted=websocket_fallback_reconnect_attempted, + websocket_fallback_started_this_call=websocket_fallback_started_this_call, + websocket_fallback_status_code=websocket_fallback_status_code, ) def to_dict(self) -> dict: @@ -6516,6 +6659,10 @@ def to_dict(self) -> dict: result["reasoningEffort"] = from_union([from_none, from_str], self.reasoning_effort) if self.request_body_bytes is not None: result["requestBodyBytes"] = from_union([from_none, to_int], self.request_body_bytes) + if self.request_body_encoding is not None: + result["requestBodyEncoding"] = from_union([from_none, lambda x: to_enum(ModelCallRequestBodyEncoding, x)], self.request_body_encoding) + if self.request_body_wire_bytes is not None: + result["requestBodyWireBytes"] = from_union([from_none, to_int], self.request_body_wire_bytes) if self.request_fingerprint is not None: result["requestFingerprint"] = from_union([from_none, lambda x: to_class(ModelCallFailureRequestFingerprint, x)], self.request_fingerprint) if self.retry_attempt is not None: @@ -6530,8 +6677,18 @@ def to_dict(self) -> dict: result["transport"] = from_union([from_none, lambda x: to_enum(ModelCallFailureTransport, x)], self.transport) if self.websocket_fallback_after is not None: result["websocketFallbackAfterMs"] = from_union([from_none, to_timedelta_int], self.websocket_fallback_after) + if self.websocket_fallback_close_code is not None: + result["websocketFallbackCloseCode"] = from_union([from_none, to_int], self.websocket_fallback_close_code) + if self.websocket_fallback_error_kind is not None: + result["websocketFallbackErrorKind"] = from_union([from_none, lambda x: to_enum(ModelCallWebSocketFallbackErrorKind, x)], self.websocket_fallback_error_kind) if self.websocket_fallback_reason is not None: result["websocketFallbackReason"] = from_union([from_none, lambda x: to_enum(ModelCallWebSocketFallbackReason, x)], self.websocket_fallback_reason) + if self.websocket_fallback_reconnect_attempted is not None: + result["websocketFallbackReconnectAttempted"] = from_union([from_none, from_bool], self.websocket_fallback_reconnect_attempted) + if self.websocket_fallback_started_this_call is not None: + result["websocketFallbackStartedThisCall"] = from_union([from_none, from_bool], self.websocket_fallback_started_this_call) + if self.websocket_fallback_status_code is not None: + result["websocketFallbackStatusCode"] = from_union([from_none, to_int], self.websocket_fallback_status_code) return result @@ -7773,6 +7930,8 @@ class PermissionRecoveryData: on_blocked: PermissionRecoveryOnBlocked reason: PermissionRecoveryReason status: PermissionRecoveryStatus + owner_agent_id: str | None = None + owner_session_id: str | None = None @staticmethod def from_dict(obj: Any) -> "PermissionRecoveryData": @@ -7783,6 +7942,8 @@ def from_dict(obj: Any) -> "PermissionRecoveryData": on_blocked = parse_enum(PermissionRecoveryOnBlocked, obj.get("onBlocked")) reason = parse_enum(PermissionRecoveryReason, obj.get("reason")) status = parse_enum(PermissionRecoveryStatus, obj.get("status")) + owner_agent_id = from_union([from_none, from_str], obj.get("ownerAgentId")) + owner_session_id = from_union([from_none, from_str], obj.get("ownerSessionId")) return PermissionRecoveryData( attempts=attempts, episode_id=episode_id, @@ -7790,6 +7951,8 @@ def from_dict(obj: Any) -> "PermissionRecoveryData": on_blocked=on_blocked, reason=reason, status=status, + owner_agent_id=owner_agent_id, + owner_session_id=owner_session_id, ) def to_dict(self) -> dict: @@ -7800,6 +7963,10 @@ def to_dict(self) -> dict: result["onBlocked"] = to_enum(PermissionRecoveryOnBlocked, self.on_blocked) result["reason"] = to_enum(PermissionRecoveryReason, self.reason) result["status"] = to_enum(PermissionRecoveryStatus, self.status) + if self.owner_agent_id is not None: + result["ownerAgentId"] = from_union([from_none, from_str], self.owner_agent_id) + if self.owner_session_id is not None: + result["ownerSessionId"] = from_union([from_none, from_str], self.owner_session_id) return result @@ -8563,6 +8730,8 @@ class PermissionRequestedData: permission_request: PermissionRequest request_id: str agent_mode: SessionMode | None = None + owner_agent_id: str | None = None + owner_session_id: str | None = None permission_mode: PermissionMode | None = None prompt_request: PermissionPromptRequest | None = None recovery_episode_id: str | None = None @@ -8575,6 +8744,8 @@ def from_dict(obj: Any) -> "PermissionRequestedData": permission_request = _load_PermissionRequest(obj.get("permissionRequest")) request_id = from_str(obj.get("requestId")) agent_mode = from_union([from_none, lambda x: parse_enum(SessionMode, x)], obj.get("agentMode")) + owner_agent_id = from_union([from_none, from_str], obj.get("ownerAgentId")) + owner_session_id = from_union([from_none, from_str], obj.get("ownerSessionId")) permission_mode = from_union([from_none, lambda x: parse_enum(PermissionMode, x)], obj.get("permissionMode")) prompt_request = from_union([from_none, _load_PermissionPromptRequest], obj.get("promptRequest")) recovery_episode_id = from_union([from_none, from_str], obj.get("recoveryEpisodeId")) @@ -8584,6 +8755,8 @@ def from_dict(obj: Any) -> "PermissionRequestedData": permission_request=permission_request, request_id=request_id, agent_mode=agent_mode, + owner_agent_id=owner_agent_id, + owner_session_id=owner_session_id, permission_mode=permission_mode, prompt_request=prompt_request, recovery_episode_id=recovery_episode_id, @@ -8597,6 +8770,10 @@ def to_dict(self) -> dict: result["requestId"] = from_str(self.request_id) if self.agent_mode is not None: result["agentMode"] = from_union([from_none, lambda x: to_enum(SessionMode, x)], self.agent_mode) + if self.owner_agent_id is not None: + result["ownerAgentId"] = from_union([from_none, from_str], self.owner_agent_id) + if self.owner_session_id is not None: + result["ownerSessionId"] = from_union([from_none, from_str], self.owner_session_id) if self.permission_mode is not None: result["permissionMode"] = from_union([from_none, lambda x: to_enum(PermissionMode, x)], self.permission_mode) if self.prompt_request is not None: @@ -9643,6 +9820,8 @@ class SessionErrorData: eligible_for_auto_switch: bool | None = None error_code: str | None = None provider_call_id: str | None = None + provider_error_param: str | None = None + provider_error_type: str | None = None remediation: RemediationAction | None = None service_request_id: str | None = None stack: str | None = None @@ -9657,6 +9836,8 @@ def from_dict(obj: Any) -> "SessionErrorData": eligible_for_auto_switch = from_union([from_none, from_bool], obj.get("eligibleForAutoSwitch")) error_code = from_union([from_none, from_str], obj.get("errorCode")) provider_call_id = from_union([from_none, from_str], obj.get("providerCallId")) + provider_error_param = from_union([from_none, from_str], obj.get("providerErrorParam")) + provider_error_type = from_union([from_none, from_str], obj.get("providerErrorType")) remediation = from_union([from_none, lambda x: parse_enum(RemediationAction, x)], obj.get("remediation")) service_request_id = from_union([from_none, from_str], obj.get("serviceRequestId")) stack = from_union([from_none, from_str], obj.get("stack")) @@ -9668,6 +9849,8 @@ def from_dict(obj: Any) -> "SessionErrorData": eligible_for_auto_switch=eligible_for_auto_switch, error_code=error_code, provider_call_id=provider_call_id, + provider_error_param=provider_error_param, + provider_error_type=provider_error_type, remediation=remediation, service_request_id=service_request_id, stack=stack, @@ -9685,6 +9868,10 @@ def to_dict(self) -> dict: result["errorCode"] = from_union([from_none, from_str], self.error_code) if self.provider_call_id is not None: result["providerCallId"] = from_union([from_none, from_str], self.provider_call_id) + if self.provider_error_param is not None: + result["providerErrorParam"] = from_union([from_none, from_str], self.provider_error_param) + if self.provider_error_type is not None: + result["providerErrorType"] = from_union([from_none, from_str], self.provider_error_type) if self.remediation is not None: result["remediation"] = from_union([from_none, lambda x: to_enum(RemediationAction, x)], self.remediation) if self.service_request_id is not None: @@ -10338,6 +10525,8 @@ class SessionPermissionRecoveryData: on_blocked: PermissionRecoveryOnBlocked reason: PermissionRecoveryReason status: PermissionRecoveryStatus + owner_agent_id: str | None = None + owner_session_id: str | None = None @staticmethod def from_dict(obj: Any) -> "SessionPermissionRecoveryData": @@ -10348,6 +10537,8 @@ def from_dict(obj: Any) -> "SessionPermissionRecoveryData": on_blocked = parse_enum(PermissionRecoveryOnBlocked, obj.get("onBlocked")) reason = parse_enum(PermissionRecoveryReason, obj.get("reason")) status = parse_enum(PermissionRecoveryStatus, obj.get("status")) + owner_agent_id = from_union([from_none, from_str], obj.get("ownerAgentId")) + owner_session_id = from_union([from_none, from_str], obj.get("ownerSessionId")) return SessionPermissionRecoveryData( attempts=attempts, episode_id=episode_id, @@ -10355,6 +10546,8 @@ def from_dict(obj: Any) -> "SessionPermissionRecoveryData": on_blocked=on_blocked, reason=reason, status=status, + owner_agent_id=owner_agent_id, + owner_session_id=owner_session_id, ) def to_dict(self) -> dict: @@ -10365,6 +10558,10 @@ def to_dict(self) -> dict: result["onBlocked"] = to_enum(PermissionRecoveryOnBlocked, self.on_blocked) result["reason"] = to_enum(PermissionRecoveryReason, self.reason) result["status"] = to_enum(PermissionRecoveryStatus, self.status) + if self.owner_agent_id is not None: + result["ownerAgentId"] = from_union([from_none, from_str], self.owner_agent_id) + if self.owner_session_id is not None: + result["ownerSessionId"] = from_union([from_none, from_str], self.owner_session_id) return result @@ -15221,6 +15418,44 @@ class ModelCallFinishedOutcome(Enum): REJECTED = "rejected" +class ModelCallRequestBodyEncoding(Enum): + "Content-Encoding applied to the request body sent on the wire" + # The request body was sent uncompressed (including every WebSocket request). + IDENTITY = "identity" + # The request body was sent gzip-compressed. + GZIP = "gzip" + # The request body was sent zstd-compressed. + ZSTD = "zstd" + + +class ModelCallWebSocketFallbackErrorKind(Enum): + "Normalized cause of the WebSocket failure that triggered the HTTP fallback" + # DNS resolution of the WebSocket host failed. + DNS = "dns" + # The TLS handshake or a TLS record failed. + TLS = "tls" + # A WebSocket connect or read timed out. + TIMEOUT = "timeout" + # The peer refused the TCP connection. + CONNECTION_REFUSED = "connection_refused" + # The connection was reset, aborted, or ended mid-stream without a close. + CONNECTION_RESET = "connection_reset" + # The peer closed the WebSocket. + CLOSED_BY_PEER = "closed_by_peer" + # The runtime closed the WebSocket itself. + CLOSED_LOCALLY = "closed_locally" + # The socket was already closed before it was used. + NOT_CONNECTED = "not_connected" + # The upgrade or request was rejected with an HTTP status. + HTTP_STATUS = "http_status" + # A WebSocket protocol violation or an undecodable frame. + PROTOCOL = "protocol" + # The WebSocket request could not be built. + CONFIGURATION = "configuration" + # A failure that maps onto none of the other kinds. + OTHER = "other" + + class ModelCallWebSocketFallbackReason(Enum): "Why a WebSocket-capable model call was carried by the HTTP fallback" # The WebSocket connection could not be established. @@ -15547,6 +15782,26 @@ class PlanChangedOperation(Enum): DELETE = "delete" +class ProviderMonthlyUsageScope(Enum): + "Identity scope of a monthly usage reading." + # Consumption of the authenticated user within the parent observation's service and returned cycle. + USER = "user" + # The service did not establish a recognized usage scope. + UNKNOWN = "unknown" + + +class ProviderMonthlyUsageState(Enum): + "Availability of a service's monthly consumption reading." + # The service reported a usable consumption reading, including an explicit zero. + AVAILABLE = "available" + # The service found no usage policy; no consumption amount is known. + NO_POLICY = "no_policy" + # The read failed or its data could not be used; consumption is unknown. + UNAVAILABLE = "unavailable" + # The service reported an unrecognized usage state. + UNKNOWN = "unknown" + + class ProviderQuotaAccessState(Enum): "Access policy reported by the quota service." # Access is allowed. @@ -15660,6 +15915,8 @@ class RemediationAction(Enum): REVIEW_SANDBOX_POLICY = "review_sandbox_policy" # Permit outbound network access in the sandbox policy. ALLOW_SANDBOX_OUTBOUND = "allow_sandbox_outbound" + # Discard conversation history back through the blocked request before continuing. + REWIND_CONVERSATION = "rewind_conversation" class SandboxAttestation(Enum): @@ -16474,6 +16731,7 @@ def session_event_to_dict(x: SessionEvent) -> Any: "IndexedSearchIncrementalPhase", "IndexedSearchOutcome", "IndexedSearchState", + "ManagedPermissionsContext", "ManagedPluginProgressPhase", "ManagedSettingsEnforcedAction", "ManagedSettingsEnforcedEscalation", @@ -16511,7 +16769,9 @@ def session_event_to_dict(x: SessionEvent) -> Any: "ModelCallFinalResultData", "ModelCallFinishedData", "ModelCallFinishedOutcome", + "ModelCallRequestBodyEncoding", "ModelCallStartData", + "ModelCallWebSocketFallbackErrorKind", "ModelCallWebSocketFallbackReason", "ModelChangeSource", "ModelDeselectedReason", @@ -16601,6 +16861,9 @@ def session_event_to_dict(x: SessionEvent) -> Any: "PersistedBinaryResult", "PlanChangedOperation", "PromptCacheBreakData", + "ProviderMonthlyUsage", + "ProviderMonthlyUsageScope", + "ProviderMonthlyUsageState", "ProviderQuotaAccessState", "ProviderQuotaAcquisitionStatus", "ProviderQuotaBudgetMetadata", diff --git a/python/copilot/session.py b/python/copilot/session.py index 9c8e0cf55f..572f5e187a 100644 --- a/python/copilot/session.py +++ b/python/copilot/session.py @@ -1373,6 +1373,7 @@ class GitHubMcpToolConfig(TypedDict, total=False): class CustomAgentConfig(TypedDict, total=False): """Configuration for a custom agent.""" + hooks: NotRequired[dict[str, list[dict[str, Any]]]] name: str # Unique name of the custom agent display_name: NotRequired[str] # Display name for UI purposes description: NotRequired[str] # Description of what the agent does @@ -1448,6 +1449,20 @@ class LargeToolOutputConfig(TypedDict, total=False): output_directory: str +class ImageGenerationConfig(TypedDict, total=False): + """**Experimental.** Opt in to an authorized Copilot image model. + + This API may change or be removed in a future release. + + Omission disables on create/cold resume and preserves resident state on + resume. Not persisted: re-supply after runtime restart. Policy, permissions, + offline mode, and tool filters still apply. BYOK is not supported. + """ + + enabled: bool + """True opts in; False disables image generation.""" + + class ToolSearchConfig(TypedDict, total=False): """ Override for the runtime's built-in tool-search behavior. diff --git a/python/e2e/test_client_options_e2e.py b/python/e2e/test_client_options_e2e.py index 1267bf6d57..701ddddd42 100644 --- a/python/e2e/test_client_options_e2e.py +++ b/python/e2e/test_client_options_e2e.py @@ -499,6 +499,7 @@ async def test_should_forward_advanced_session_options_in_create_wire_request( try: await client.start() session = await client.create_session( + name="python named at create", client_name="advanced-create-client", model="claude-sonnet-5", reasoning_effort="medium", @@ -585,6 +586,7 @@ async def test_should_forward_advanced_session_options_in_create_wire_request( ) try: params = _get_captured_request(capture_path, "session.create") + assert params["name"] == "python named at create" assert params["clientName"] == "advanced-create-client" assert params["model"] == "claude-sonnet-5" assert params["reasoningEffort"] == "medium" diff --git a/python/test_client.py b/python/test_client.py index 5badf26ee6..95e80c1b0e 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -16,6 +16,7 @@ import pytest +import copilot.client as client_module from copilot import ( CanvasProviderIdentity, CapiSessionOptions, @@ -87,6 +88,29 @@ def test_inprocess_connection_has_no_child_process_options(): assert not hasattr(connection, "args") +@pytest.mark.asyncio +async def test_inprocess_forwards_integration_id_to_native_host( + monkeypatch: pytest.MonkeyPatch, tmp_path +): + monkeypatch.setenv("GITHUB_COPILOT_INTEGRATION_ID", " microsoft/vscode ") + monkeypatch.setattr( + "copilot._cli_download.ensure_runtime_wrapper", + lambda: str(tmp_path / "copilot-runtime"), + ) + host = Mock() + host.process = Mock() + host.start_blocking = Mock() + create_host = Mock(return_value=host) + monkeypatch.setattr(client_module.FfiRuntimeHost, "create", create_host) + client = CopilotClient(connection=RuntimeConnection.for_inprocess()) + + await client._start_inprocess_ffi() + + assert ( + "--cli-login-sync-integration-id=microsoft/vscode" in create_host.call_args.kwargs["args"] + ) + + def test_explicit_child_process_path_does_not_require_runtime_bundle(tmp_path): explicit = tmp_path / "copilot" connection = RuntimeConnection.for_stdio(path=str(explicit)) diff --git a/python/test_customization_directories.py b/python/test_customization_directories.py new file mode 100644 index 0000000000..254025a34f --- /dev/null +++ b/python/test_customization_directories.py @@ -0,0 +1,71 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. + +from unittest.mock import AsyncMock + +import pytest + +from copilot import CopilotClient, RuntimeConnection +from copilot._jsonrpc import JsonRpcClient + + +@pytest.mark.asyncio +@pytest.mark.parametrize("roots", [None, [], ["relative-root", "other-root"]]) +async def test_customization_roots_preserved_on_create_and_resume(monkeypatch, roots): + client = CopilotClient(connection=RuntimeConnection.for_uri("localhost:1234")) + rpc = AsyncMock(spec=JsonRpcClient) + requests = {} + + async def request(method, params, **kwargs): + requests[method] = params + if method in ("session.create", "session.resume"): + response = {"sessionId": params["sessionId"]} + if callback := kwargs.get("on_response_inline"): + callback(response) + return response + return {} + + rpc.request.side_effect = request + monkeypatch.setattr(client, "_client", rpc) + skills = None if roots is None else [f"{root}-skills" for root in roots] + agents = None if roots is None else [f"{root}-agents" for root in roots] + instructions = None if roots is None else [f"{root}-instructions" for root in roots] + hooks = None if roots is None else [f"{root}-hooks" for root in roots] + try: + session = await client.create_session( + working_directory="request-cwd", + enable_config_discovery=False, + enable_file_hooks=False, + skill_directories=skills, + agent_directories=agents, + instruction_directories=instructions, + hook_directories=hooks, + ) + await client.resume_session( + session.session_id, + working_directory="resume-cwd", + enable_config_discovery=False, + enable_file_hooks=False, + skill_directories=skills, + agent_directories=agents, + instruction_directories=instructions, + hook_directories=hooks, + ) + for method in ("session.create", "session.resume"): + payload = requests[method] + for key, expected in ( + ("skillDirectories", skills), + ("agentDirectories", agents), + ("instructionDirectories", instructions), + ("hookDirectories", hooks), + ): + if expected is None: + assert key not in payload + else: + assert payload[key] == expected + assert payload["enableConfigDiscovery"] is False + assert payload["enableFileHooks"] is False + assert "additionalDirectories" not in payload + assert "customAgents" not in payload + assert "hooks" not in payload + finally: + await client.force_stop() diff --git a/python/test_image_generation.py b/python/test_image_generation.py new file mode 100644 index 0000000000..7323d869da --- /dev/null +++ b/python/test_image_generation.py @@ -0,0 +1,47 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. + +"""Image generation request serialization without starting a runtime.""" + +import json +from unittest.mock import AsyncMock, Mock + +import pytest + +from copilot import CopilotClient, ImageGenerationConfig, PermissionHandler, RuntimeConnection + + +@pytest.mark.asyncio +@pytest.mark.parametrize("option", [None, {}, {"enabled": True}, {"enabled": False}]) +async def test_image_generation_create_and_resume_wire(option: ImageGenerationConfig | None): + client = CopilotClient(connection=RuntimeConnection.for_stdio(path="unused-cli")) + client._state = "connected" + transport = Mock() + captured = {} + + async def request(method, params, **kwargs): + captured[method] = json.loads(json.dumps(params)) + response = {"sessionId": params["sessionId"]} + if callback := kwargs.get("on_response_inline"): + callback(response) + return response + + transport.request = AsyncMock(side_effect=request) + transport.stop = AsyncMock() + client._client = transport + try: + session = await client.create_session( + on_permission_request=PermissionHandler.approve_all, + image_generation=option, + ) + await client.resume_session( + session.session_id, + on_permission_request=PermissionHandler.approve_all, + image_generation=option, + ) + for method in ("session.create", "session.resume"): + if option is None: + assert "imageGeneration" not in captured[method], captured[method] + else: + assert captured[method]["imageGeneration"] == option + finally: + await client.force_stop() diff --git a/rust/Cargo.toml b/rust/Cargo.toml index b82e322b02..46b818bb61 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -81,8 +81,10 @@ tokio-tungstenite = { version = "0.28.0", default-features = false, features = [ zip = { version = "7.2.0", default-features = false, features = ["deflate"], optional = true } windows-sys = { version = "0.61", default-features = false, features = [ "Win32_Foundation", - "Win32_System_Diagnostics_ToolHelp", + "Win32_System_Diagnostics_Debug", + "Win32_System_Diagnostics_ProcessSnapshotting", "Win32_System_JobObjects", + "Win32_System_Kernel", "Win32_System_Threading", ] } diff --git a/rust/README.md b/rust/README.md index 87484defec..f233c8b14e 100644 --- a/rust/README.md +++ b/rust/README.md @@ -124,6 +124,12 @@ Transport-specific phases are optional. For example, `port_wait_ms` is present only for TCP and `process_spawn_ms` is absent for external and in-process transports. +On Windows, SDK-spawned CLI processes start suspended and are assigned to a +private kill-on-close Job Object before their initial thread resumes. Thread +lookup captures only that child's process snapshot, rather than enumerating +machine-wide threads. The Job owns the CLI and its descendants until client +shutdown or final-client drop, including abrupt termination of the SDK host. + **`ClientOptions`:** | Field | Type | Description | diff --git a/rust/bazel/crates/BUILD.windows-sys-0.61.2.bazel b/rust/bazel/crates/BUILD.windows-sys-0.61.2.bazel index f964882760..87b0fb6806 100644 --- a/rust/bazel/crates/BUILD.windows-sys-0.61.2.bazel +++ b/rust/bazel/crates/BUILD.windows-sys-0.61.2.bazel @@ -54,9 +54,11 @@ rust_library( "Win32_Storage_FileSystem", "Win32_System", "Win32_System_Diagnostics", - "Win32_System_Diagnostics_ToolHelp", + "Win32_System_Diagnostics_Debug", + "Win32_System_Diagnostics_ProcessSnapshotting", "Win32_System_IO", "Win32_System_JobObjects", + "Win32_System_Kernel", "Win32_System_LibraryLoader", "Win32_System_Memory", "Win32_System_Pipes", diff --git a/rust/src/ahp_host.rs b/rust/src/ahp_host.rs index 7ae2f1ca4e..5c906f605c 100644 --- a/rust/src/ahp_host.rs +++ b/rust/src/ahp_host.rs @@ -936,6 +936,8 @@ macro_rules! host_settings { "memory" => memory, "pluginDirectories" => plugin_directories, "skillDirectories" => skill_directories, + "agentDirectories" => agent_directories, + "hookDirectories" => hook_directories, "instructionDirectories" => instruction_directories, "enableMcpApps" => enable_mcp_apps, "model" => model, diff --git a/rust/src/ahp_host/factory_tests.rs b/rust/src/ahp_host/factory_tests.rs index bb328c7acd..a4b9cf3751 100644 --- a/rust/src/ahp_host/factory_tests.rs +++ b/rust/src/ahp_host/factory_tests.rs @@ -629,6 +629,7 @@ fn host_settings_round_trip_without_callbacks_or_losing_constraints() { "customAgentsLocalOnly": true, "coauthorEnabled": false, "manageScheduleEnabled": false, "memory": {"enabled":false}, "pluginDirectories": ["/plugins"], "skillDirectories": ["/skills"], "instructionDirectories": ["/instructions"], + "agentDirectories": ["relative-agents"], "hookDirectories": [], "enableMcpApps": true, "model":"model", "reasoningEffort":"high", "contextTier":"long_context" }); let config = config_from_host(&serde_json::from_value(value.clone()).unwrap()).unwrap(); diff --git a/rust/src/copilot_request_handler.rs b/rust/src/copilot_request_handler.rs index 381acac0d2..c0dec20a97 100644 --- a/rust/src/copilot_request_handler.rs +++ b/rust/src/copilot_request_handler.rs @@ -718,7 +718,13 @@ impl CopilotRequestExchange { let mut buf = Vec::new(); let mut rx = self.body_rx.lock().await; while let Some(frame) = rx.recv().await { - buf.extend_from_slice(&frame); + // The first frame becomes the buffer, so a body sent as one chunk (the runtime sends + // every HTTP request body so) reaches the handler without a copy. + if buf.is_empty() { + buf = frame; + } else { + buf.extend_from_slice(&frame); + } } buf } @@ -1114,7 +1120,8 @@ impl CopilotRequestDispatcher { async fn handle_start(self: &Arc, request: JsonRpcRequest) { let id = request.id; - let Some(params) = parse_params::(&request) else { + let Some(params) = parse_params::(request.params) + else { self.send_error(id, "invalid llmInference.httpRequestStart params") .await; return; @@ -1140,7 +1147,8 @@ impl CopilotRequestDispatcher { async fn handle_chunk(&self, request: JsonRpcRequest) { let id = request.id; - let Some(params) = parse_params::(&request) else { + let Some(params) = parse_params::(request.params) + else { self.send_error(id, "invalid llmInference.httpRequestChunk params") .await; return; @@ -1149,7 +1157,7 @@ impl CopilotRequestDispatcher { // May arrive before the matching start frame; get-or-create so the body // is buffered, never lost. let exchange = self.get_or_create_exchange(params.request_id.to_string()); - apply_chunk(&exchange, ¶ms); + apply_chunk(&exchange, params); self.ack(id).await; } @@ -1193,7 +1201,7 @@ impl CopilotRequestDispatcher { /// Apply one body chunk to a pending request: route data into the body stream, /// or terminate it on `end` / `cancel`. -fn apply_chunk(exchange: &CopilotRequestExchange, params: &LlmInferenceHttpRequestChunkRequest) { +fn apply_chunk(exchange: &CopilotRequestExchange, params: LlmInferenceHttpRequestChunkRequest) { if params.cancel == Some(true) { exchange.push_cancel(); return; @@ -1209,7 +1217,7 @@ fn apply_chunk(exchange: &CopilotRequestExchange, params: &LlmInferenceHttpReque } } } else { - params.data.clone().into_bytes() + params.data.into_bytes() }; exchange.push_chunk(decoded); } @@ -1219,11 +1227,10 @@ fn apply_chunk(exchange: &CopilotRequestExchange, params: &LlmInferenceHttpReque } } -fn parse_params(request: &JsonRpcRequest) -> Option { - request - .params - .as_ref() - .and_then(|p| serde_json::from_value(p.clone()).ok()) +/// Deserialize a request's params by value, so their strings (the request body among them) move +/// into `T`: deserializing from a borrow deep-clones the whole `Value` first. +fn parse_params(params: Option) -> Option { + params.and_then(|p| serde_json::from_value(p).ok()) } /// Convert a wire header map into an [`http::HeaderMap`], skipping any entry the diff --git a/rust/src/ffi.rs b/rust/src/ffi.rs index df0269b0b8..3d7bbf4401 100644 --- a/rust/src/ffi.rs +++ b/rust/src/ffi.rs @@ -245,6 +245,7 @@ impl AsyncWrite for FfiWriter { pub(crate) struct FfiHost { library_path: PathBuf, cli_entrypoint: Option, + integration_id: Option, environment: Vec<(String, String)>, args: Vec, host_start: HostStartFn, @@ -263,6 +264,7 @@ impl FfiHost { pub(crate) fn create( runtime_entrypoint: &Path, cli_entrypoint: Option<&Path>, + integration_id: Option<&str>, environment: Vec<(String, String)>, args: Vec, ) -> Result { @@ -307,6 +309,7 @@ impl FfiHost { Ok(Self { library_path, cli_entrypoint, + integration_id: integration_id.map(str::to_owned), environment, args, host_start, @@ -330,7 +333,11 @@ impl FfiHost { } fn start_blocking(self) -> Result<(FfiReader, FfiWriter, Arc), Error> { - let argv = build_argv_json(self.cli_entrypoint.as_deref(), &self.args); + let argv = build_argv_json( + self.cli_entrypoint.as_deref(), + self.integration_id.as_deref(), + &self.args, + ); let env = build_env_json(&self.environment); let (env_ptr, env_len) = match &env { @@ -561,7 +568,11 @@ fn path_for_child_process(path: PathBuf) -> PathBuf { path } -fn build_argv_json(entrypoint: Option<&Path>, extra_args: &[String]) -> Vec { +fn build_argv_json( + entrypoint: Option<&Path>, + integration_id: Option<&str>, + extra_args: &[String], +) -> Vec { let mut argv = Vec::new(); if let Some(entrypoint) = entrypoint { let entrypoint_str = entrypoint.to_string_lossy().into_owned(); @@ -578,6 +589,9 @@ fn build_argv_json(entrypoint: Option<&Path>, extra_args: &[String]) -> Vec "--no-auto-update".to_string(), ]); } + if let Some(integration_id) = integration_id { + argv.push(format!("--cli-login-sync-integration-id={integration_id}")); + } argv.extend_from_slice(extra_args); serde_json::to_vec(&argv).expect("argv serializes") } diff --git a/rust/src/ffi/tests.rs b/rust/src/ffi/tests.rs index 5662dc4e5b..5fbd0929fc 100644 --- a/rust/src/ffi/tests.rs +++ b/rust/src/ffi/tests.rs @@ -36,6 +36,7 @@ unsafe extern "C" fn test_connection_close(_connection_id: u32) -> bool { #[test] fn argv_without_entrypoint_contains_only_client_options() { let argv: Vec = serde_json::from_slice(&build_argv_json( + None, None, &["--log-level".into(), "debug".into()], )) @@ -46,12 +47,22 @@ fn argv_without_entrypoint_contains_only_client_options() { #[test] fn explicit_javascript_entrypoint_uses_node() { - let argv: Vec = - serde_json::from_slice(&build_argv_json(Some(Path::new("index.js")), &[])).unwrap(); + let argv: Vec = serde_json::from_slice(&build_argv_json( + Some(Path::new("index.js")), + Some("microsoft/vscode"), + &[], + )) + .unwrap(); assert_eq!( argv, - ["node", "index.js", "--embedded-host", "--no-auto-update"] + [ + "node", + "index.js", + "--embedded-host", + "--no-auto-update", + "--cli-login-sync-integration-id=microsoft/vscode", + ] ); } diff --git a/rust/src/generated/api_types.rs b/rust/src/generated/api_types.rs index 31cc1b82d7..823e5055a0 100644 --- a/rust/src/generated/api_types.rs +++ b/rust/src/generated/api_types.rs @@ -11,17 +11,182 @@ use serde::{Deserialize, Serialize}; pub use super::session_events::{ AbortReason, AgentModelPolicy, AutoTier, ContextTier, IndexedSearchState, - ManagedSettingsResolvedSource, McpOauthHttpResponse, McpOauthWWWAuthenticateParams, - McpServerMetadata, McpServerSource, McpServerStatus, ModelChangeSource, ModelProviderKind, - ModelProviderRef, OmittedBinaryOmittedReason, PermissionDecisionSource, PermissionMode, - PermissionPromptRequest, PermissionRule, ProviderQuotaState, ReasoningSummary, - RemediationAction, SessionLimitsConfig, SessionMode, ShutdownType, SkillSource, TaskBlocker, - TaskCompletionOutcome, UsageGetMetricsResult, UserToolSessionApproval, Verbosity, + ManagedPermissionsContext, ManagedSettingsResolvedSource, McpOauthHttpResponse, + McpOauthWWWAuthenticateParams, McpServerMetadata, McpServerSource, McpServerStatus, + ModelChangeSource, ModelProviderKind, ModelProviderRef, OmittedBinaryOmittedReason, + PermissionDecisionSource, PermissionMode, PermissionPromptRequest, PermissionRule, + ProviderQuotaState, ReasoningSummary, RemediationAction, SessionLimitsConfig, SessionMode, + ShutdownType, SkillSource, TaskBlocker, TaskCompletionOutcome, UsageGetMetricsResult, + UserToolSessionApproval, Verbosity, }; use crate::types::{RequestId, SessionEvent, SessionId}; /// JSON-RPC method name constants. pub mod rpc_methods { + /// `sandbox.proxyCa.checkInstallationSupport` + pub const SANDBOX_PROXYCA_CHECKINSTALLATIONSUPPORT: &str = + "sandbox.proxyCa.checkInstallationSupport"; + /// `sandbox.proxyCa.checkRelevance` + pub const SANDBOX_PROXYCA_CHECKRELEVANCE: &str = "sandbox.proxyCa.checkRelevance"; + /// `sandbox.proxyCa.getStatus` + pub const SANDBOX_PROXYCA_GETSTATUS: &str = "sandbox.proxyCa.getStatus"; + /// `sandbox.proxyCa.create` + pub const SANDBOX_PROXYCA_CREATE: &str = "sandbox.proxyCa.create"; + /// `sandbox.proxyCa.rotate` + pub const SANDBOX_PROXYCA_ROTATE: &str = "sandbox.proxyCa.rotate"; + /// `sandbox.proxyCa.trust` + pub const SANDBOX_PROXYCA_TRUST: &str = "sandbox.proxyCa.trust"; + /// `sandbox.proxyCa.remove` + pub const SANDBOX_PROXYCA_REMOVE: &str = "sandbox.proxyCa.remove"; + /// `sandbox.proxySecret.getLocation` + pub const SANDBOX_PROXYSECRET_GETLOCATION: &str = "sandbox.proxySecret.getLocation"; + /// `sandbox.proxySecret.get` + pub const SANDBOX_PROXYSECRET_GET: &str = "sandbox.proxySecret.get"; + /// `sandbox.proxySecret.save` + pub const SANDBOX_PROXYSECRET_SAVE: &str = "sandbox.proxySecret.save"; + /// `sandbox.proxySecret.delete` + pub const SANDBOX_PROXYSECRET_DELETE: &str = "sandbox.proxySecret.delete"; + /// `sandbox.settings.applyDefaults` + pub const SANDBOX_SETTINGS_APPLYDEFAULTS: &str = "sandbox.settings.applyDefaults"; + /// `sandbox.resolveFlooredConfig` + pub const SANDBOX_RESOLVEFLOOREDCONFIG: &str = "sandbox.resolveFlooredConfig"; + /// `sandbox.preparePickerPolicy` + pub const SANDBOX_PREPAREPICKERPOLICY: &str = "sandbox.preparePickerPolicy"; + /// `sandbox.filterPickerCandidates` + pub const SANDBOX_FILTERPICKERCANDIDATES: &str = "sandbox.filterPickerCandidates"; + /// `sandbox.resolveForOptional` + pub const SANDBOX_RESOLVEFOROPTIONAL: &str = "sandbox.resolveForOptional"; + /// `sandbox.checkPlatformSupport` + pub const SANDBOX_CHECKPLATFORMSUPPORT: &str = "sandbox.checkPlatformSupport"; + /// `sandbox.checkWindowsHostLoopbackSupport` + pub const SANDBOX_CHECKWINDOWSHOSTLOOPBACKSUPPORT: &str = + "sandbox.checkWindowsHostLoopbackSupport"; + /// `sandbox.getUnsupportedMessage` + pub const SANDBOX_GETUNSUPPORTEDMESSAGE: &str = "sandbox.getUnsupportedMessage"; + /// `sandbox.getUnsupportedSettingDisabledNotice` + pub const SANDBOX_GETUNSUPPORTEDSETTINGDISABLEDNOTICE: &str = + "sandbox.getUnsupportedSettingDisabledNotice"; + /// `sandbox.getUnsupportedStartupNotice` + pub const SANDBOX_GETUNSUPPORTEDSTARTUPNOTICE: &str = "sandbox.getUnsupportedStartupNotice"; + /// `sandbox.checkEnforcementCommand` + pub const SANDBOX_CHECKENFORCEMENTCOMMAND: &str = "sandbox.checkEnforcementCommand"; + /// `sandbox.getDocsUrl` + pub const SANDBOX_GETDOCSURL: &str = "sandbox.getDocsUrl"; + /// `sandbox.getHostSupport` + pub const SANDBOX_GETHOSTSUPPORT: &str = "sandbox.getHostSupport"; + /// `persistence.writeRawJsonLocked` + pub const PERSISTENCE_WRITERAWJSONLOCKED: &str = "persistence.writeRawJsonLocked"; + /// `user.settings.setHookDisabled` + pub const USER_SETTINGS_SETHOOKDISABLED: &str = "user.settings.setHookDisabled"; + /// `user.settings.loadForConfigDir` + pub const USER_SETTINGS_LOADFORCONFIGDIR: &str = "user.settings.loadForConfigDir"; + /// `user.settings.loadRepo` + pub const USER_SETTINGS_LOADREPO: &str = "user.settings.loadRepo"; + /// `user.settings.loadAndMergeRepo` + pub const USER_SETTINGS_LOADANDMERGEREPO: &str = "user.settings.loadAndMergeRepo"; + /// `user.settings.getPath` + pub const USER_SETTINGS_GETPATH: &str = "user.settings.getPath"; + /// `user.settings.loadWithWarning` + pub const USER_SETTINGS_LOADWITHWARNING: &str = "user.settings.loadWithWarning"; + /// `user.settings.mergeManagedAndUser` + pub const USER_SETTINGS_MERGEMANAGEDANDUSER: &str = "user.settings.mergeManagedAndUser"; + /// `user.settings.mergeRepoValue` + pub const USER_SETTINGS_MERGEREPOVALUE: &str = "user.settings.mergeRepoValue"; + /// `user.settings.migrateStaffUpdateChannelOnce` + pub const USER_SETTINGS_MIGRATESTAFFUPDATECHANNELONCE: &str = + "user.settings.migrateStaffUpdateChannelOnce"; + /// `user.settings.resetStaffModelSelectionOnce` + pub const USER_SETTINGS_RESETSTAFFMODELSELECTIONONCE: &str = + "user.settings.resetStaffModelSelectionOnce"; + /// `user.settings.getUnknownKeysWarning` + pub const USER_SETTINGS_GETUNKNOWNKEYSWARNING: &str = "user.settings.getUnknownKeysWarning"; + /// `user.settings.buildRuntime` + pub const USER_SETTINGS_BUILDRUNTIME: &str = "user.settings.buildRuntime"; + /// `user.settings.migrateStaffLogLevelOnce` + pub const USER_SETTINGS_MIGRATESTAFFLOGLEVELONCE: &str = + "user.settings.migrateStaffLogLevelOnce"; + /// `user.settings.migrateObsoleteOnDisk` + pub const USER_SETTINGS_MIGRATEOBSOLETEONDISK: &str = "user.settings.migrateObsoleteOnDisk"; + /// `user.settings.get` + pub const USER_SETTINGS_GET: &str = "user.settings.get"; + /// `user.settings.getPaths` + pub const USER_SETTINGS_GETPATHS: &str = "user.settings.getPaths"; + /// `user.settings.set` + pub const USER_SETTINGS_SET: &str = "user.settings.set"; + /// `user.settings.listGovernanceKeys` + pub const USER_SETTINGS_LISTGOVERNANCEKEYS: &str = "user.settings.listGovernanceKeys"; + /// `user.settings.edit` + pub const USER_SETTINGS_EDIT: &str = "user.settings.edit"; + /// `user.settings.normalize` + pub const USER_SETTINGS_NORMALIZE: &str = "user.settings.normalize"; + /// `user.settings.diagnoseProblems` + pub const USER_SETTINGS_DIAGNOSEPROBLEMS: &str = "user.settings.diagnoseProblems"; + /// `user.settings.applyTopKey` + pub const USER_SETTINGS_APPLYTOPKEY: &str = "user.settings.applyTopKey"; + /// `user.settings.writeAll` + pub const USER_SETTINGS_WRITEALL: &str = "user.settings.writeAll"; + /// `user.settings.update` + pub const USER_SETTINGS_UPDATE: &str = "user.settings.update"; + /// `user.settings.loadForEdit` + pub const USER_SETTINGS_LOADFOREDIT: &str = "user.settings.loadForEdit"; + /// `user.settings.validate` + pub const USER_SETTINGS_VALIDATE: &str = "user.settings.validate"; + /// `repo.settings.edit` + pub const REPO_SETTINGS_EDIT: &str = "repo.settings.edit"; + /// `repo.settings.readRaw` + pub const REPO_SETTINGS_READRAW: &str = "repo.settings.readRaw"; + /// `repo.settings.writeKey` + pub const REPO_SETTINGS_WRITEKEY: &str = "repo.settings.writeKey"; + /// `repo.settings.writeAll` + pub const REPO_SETTINGS_WRITEALL: &str = "repo.settings.writeAll"; + /// `featureFlags.resolveStartupGates` + pub const FEATUREFLAGS_RESOLVESTARTUPGATES: &str = "featureFlags.resolveStartupGates"; + /// `featureFlags.listMetadata` + pub const FEATUREFLAGS_LISTMETADATA: &str = "featureFlags.listMetadata"; + /// `featureFlags.resolve` + pub const FEATUREFLAGS_RESOLVE: &str = "featureFlags.resolve"; + /// `permissions.locations.cleanupOrphaned` + pub const PERMISSIONS_LOCATIONS_CLEANUPORPHANED: &str = "permissions.locations.cleanupOrphaned"; + /// `permissions.folderTrust.checkTrust` + pub const PERMISSIONS_FOLDERTRUST_CHECKTRUST: &str = "permissions.folderTrust.checkTrust"; + /// `permissions.evaluateManagedPolicy` + pub const PERMISSIONS_EVALUATEMANAGEDPOLICY: &str = "permissions.evaluateManagedPolicy"; + /// `managedSettings.readDevice` + pub const MANAGEDSETTINGS_READDEVICE: &str = "managedSettings.readDevice"; + /// `managedSettings.retainPolicy` + pub const MANAGEDSETTINGS_RETAINPOLICY: &str = "managedSettings.retainPolicy"; + /// `managedSettings.applyControls` + pub const MANAGEDSETTINGS_APPLYCONTROLS: &str = "managedSettings.applyControls"; + /// `managedSettings.parseClientValue` + pub const MANAGEDSETTINGS_PARSECLIENTVALUE: &str = "managedSettings.parseClientValue"; + /// `managedSettings.resolveEffectiveValue` + pub const MANAGEDSETTINGS_RESOLVEEFFECTIVEVALUE: &str = "managedSettings.resolveEffectiveValue"; + /// `managedSettings.resolveBypassDisableTransition` + pub const MANAGEDSETTINGS_RESOLVEBYPASSDISABLETRANSITION: &str = + "managedSettings.resolveBypassDisableTransition"; + /// `managedSettings.ingest` + pub const MANAGEDSETTINGS_INGEST: &str = "managedSettings.ingest"; + /// `managedSettings.resolveStartupPolicy` + pub const MANAGEDSETTINGS_RESOLVESTARTUPPOLICY: &str = "managedSettings.resolveStartupPolicy"; + /// `managedSettings.loadRemoteControl` + pub const MANAGEDSETTINGS_LOADREMOTECONTROL: &str = "managedSettings.loadRemoteControl"; + /// `managedSettings.resolveDeviceForceLoginOrgsGate` + pub const MANAGEDSETTINGS_RESOLVEDEVICEFORCELOGINORGSGATE: &str = + "managedSettings.resolveDeviceForceLoginOrgsGate"; + /// `managedSettings.read` + pub const MANAGEDSETTINGS_READ: &str = "managedSettings.read"; + /// `managedSettings.clearCache` + pub const MANAGEDSETTINGS_CLEARCACHE: &str = "managedSettings.clearCache"; + /// `managedSettings.resolve` + pub const MANAGEDSETTINGS_RESOLVE: &str = "managedSettings.resolve"; + /// `managedSettings.permissions.evaluate` + pub const MANAGEDSETTINGS_PERMISSIONS_EVALUATE: &str = "managedSettings.permissions.evaluate"; + /// `managedSettings.schema` + pub const MANAGEDSETTINGS_SCHEMA: &str = "managedSettings.schema"; + /// `managedSettings.validate` + pub const MANAGEDSETTINGS_VALIDATE: &str = "managedSettings.validate"; + /// `managedSettings.compose` + pub const MANAGEDSETTINGS_COMPOSE: &str = "managedSettings.compose"; /// `environments.list` pub const ENVIRONMENTS_LIST: &str = "environments.list"; /// `environments.get` @@ -56,18 +221,6 @@ pub mod rpc_methods { pub const MODELS_LIST: &str = "models.list"; /// `models.getBuiltInCatalog` pub const MODELS_GETBUILTINCATALOG: &str = "models.getBuiltInCatalog"; - /// `sandbox.getHostSupport` - pub const SANDBOX_GETHOSTSUPPORT: &str = "sandbox.getHostSupport"; - /// `sandbox.proxyCa.getStatus` - pub const SANDBOX_PROXYCA_GETSTATUS: &str = "sandbox.proxyCa.getStatus"; - /// `sandbox.proxyCa.create` - pub const SANDBOX_PROXYCA_CREATE: &str = "sandbox.proxyCa.create"; - /// `sandbox.proxyCa.rotate` - pub const SANDBOX_PROXYCA_ROTATE: &str = "sandbox.proxyCa.rotate"; - /// `sandbox.proxyCa.trust` - pub const SANDBOX_PROXYCA_TRUST: &str = "sandbox.proxyCa.trust"; - /// `sandbox.proxyCa.remove` - pub const SANDBOX_PROXYCA_REMOVE: &str = "sandbox.proxyCa.remove"; /// `tools.list` pub const TOOLS_LIST: &str = "tools.list"; /// `account.getQuota` @@ -80,6 +233,14 @@ pub mod rpc_methods { pub const ACCOUNT_LOGIN: &str = "account.login"; /// `account.logout` pub const ACCOUNT_LOGOUT: &str = "account.logout"; + /// `account.describeAuthInfo` + pub const ACCOUNT_DESCRIBEAUTHINFO: &str = "account.describeAuthInfo"; + /// `account.getGistSupport` + pub const ACCOUNT_GETGISTSUPPORT: &str = "account.getGistSupport"; + /// `account.getWebUrl` + pub const ACCOUNT_GETWEBURL: &str = "account.getWebUrl"; + /// `account.getTokenEnvVar` + pub const ACCOUNT_GETTOKENENVVAR: &str = "account.getTokenEnvVar"; /// `secrets.addFilterValues` pub const SECRETS_ADDFILTERVALUES: &str = "secrets.addFilterValues"; /// `mcp.config.list` @@ -96,8 +257,18 @@ pub mod rpc_methods { pub const MCP_CONFIG_DISABLE: &str = "mcp.config.disable"; /// `mcp.config.reload` pub const MCP_CONFIG_RELOAD: &str = "mcp.config.reload"; + /// `mcp.registry.allocateRequestId` + pub const MCP_REGISTRY_ALLOCATEREQUESTID: &str = "mcp.registry.allocateRequestId"; + /// `mcp.registry.search` + pub const MCP_REGISTRY_SEARCH: &str = "mcp.registry.search"; + /// `mcp.registry.cancel` + pub const MCP_REGISTRY_CANCEL: &str = "mcp.registry.cancel"; /// `mcp.discover` pub const MCP_DISCOVER: &str = "mcp.discover"; + /// `mcp.listBuiltinServerNames` + pub const MCP_LISTBUILTINSERVERNAMES: &str = "mcp.listBuiltinServerNames"; + /// `mcp.shouldExcludeGitHubTools` + pub const MCP_SHOULDEXCLUDEGITHUBTOOLS: &str = "mcp.shouldExcludeGitHubTools"; /// `mcp.planInstall` pub const MCP_PLANINSTALL: &str = "mcp.planInstall"; /// `mcp.prepareInstall` @@ -122,6 +293,16 @@ pub mod rpc_methods { pub const EXTENSIONS_ENABLE: &str = "extensions.enable"; /// `extensions.disable` pub const EXTENSIONS_DISABLE: &str = "extensions.disable"; + /// `instructions.getInitPrompt` + pub const INSTRUCTIONS_GETINITPROMPT: &str = "instructions.getInitPrompt"; + /// `instructions.discover` + pub const INSTRUCTIONS_DISCOVER: &str = "instructions.discover"; + /// `instructions.discoverRepoSources` + pub const INSTRUCTIONS_DISCOVERREPOSOURCES: &str = "instructions.discoverRepoSources"; + /// `instructions.getDiscoveryPaths` + pub const INSTRUCTIONS_GETDISCOVERYPATHS: &str = "instructions.getDiscoveryPaths"; + /// `instructions.clearCache` + pub const INSTRUCTIONS_CLEARCACHE: &str = "instructions.clearCache"; /// `skills.planInstall` pub const SKILLS_PLANINSTALL: &str = "skills.planInstall"; /// `skills.applyInstall` @@ -195,10 +376,6 @@ pub mod rpc_methods { /// `agents.customAgentInitialModelDecision` pub const AGENTS_CUSTOMAGENTINITIALMODELDECISION: &str = "agents.customAgentInitialModelDecision"; - /// `instructions.discover` - pub const INSTRUCTIONS_DISCOVER: &str = "instructions.discover"; - /// `instructions.getDiscoveryPaths` - pub const INSTRUCTIONS_GETDISCOVERYPATHS: &str = "instructions.getDiscoveryPaths"; /// `globalState.load` pub const GLOBALSTATE_LOAD: &str = "globalState.load"; /// `globalState.loadForConfigDir` @@ -207,10 +384,6 @@ pub mod rpc_methods { pub const GLOBALSTATE_WRITEKEY: &str = "globalState.writeKey"; /// `commands.list` pub const COMMANDS_LIST: &str = "commands.list"; - /// `user.settings.get` - pub const USER_SETTINGS_GET: &str = "user.settings.get"; - /// `user.settings.set` - pub const USER_SETTINGS_SET: &str = "user.settings.set"; /// `gitHubRepository.atPath` pub const GITHUBREPOSITORY_ATPATH: &str = "gitHubRepository.atPath"; /// `gitHubOwners.nextRequestId` @@ -225,18 +398,6 @@ pub mod rpc_methods { pub const GIT_WORKINGDIRECTORYCONTEXT: &str = "git.workingDirectoryContext"; /// `git.reposFromRemotes` pub const GIT_REPOSFROMREMOTES: &str = "git.reposFromRemotes"; - /// `managedSettings.read` - pub const MANAGEDSETTINGS_READ: &str = "managedSettings.read"; - /// `managedSettings.clearCache` - pub const MANAGEDSETTINGS_CLEARCACHE: &str = "managedSettings.clearCache"; - /// `managedSettings.resolve` - pub const MANAGEDSETTINGS_RESOLVE: &str = "managedSettings.resolve"; - /// `managedSettings.schema` - pub const MANAGEDSETTINGS_SCHEMA: &str = "managedSettings.schema"; - /// `managedSettings.validate` - pub const MANAGEDSETTINGS_VALIDATE: &str = "managedSettings.validate"; - /// `managedSettings.compose` - pub const MANAGEDSETTINGS_COMPOSE: &str = "managedSettings.compose"; /// `runtime.shutdown` pub const RUNTIME_SHUTDOWN: &str = "runtime.shutdown"; /// `sessionFs.setProvider` @@ -346,6 +507,9 @@ pub mod rpc_methods { pub const SESSION_SENDMESSAGES: &str = "session.sendMessages"; /// `session.sandbox.getEnforcementStatus` pub const SESSION_SANDBOX_GETENFORCEMENTSTATUS: &str = "session.sandbox.getEnforcementStatus"; + /// `session.sandbox.getCredentialSuggestions` + pub const SESSION_SANDBOX_GETCREDENTIALSUGGESTIONS: &str = + "session.sandbox.getCredentialSuggestions"; /// `session.sandbox.disableForSession` pub const SESSION_SANDBOX_DISABLEFORSESSION: &str = "session.sandbox.disableForSession"; /// `session.sandbox.grantPathForRequest` @@ -453,6 +617,8 @@ pub mod rpc_methods { pub const SESSION_MODEL_SETALLOWEDMODELS: &str = "session.model.setAllowedModels"; /// `session.model.setReasoningEffort` pub const SESSION_MODEL_SETREASONINGEFFORT: &str = "session.model.setReasoningEffort"; + /// `session.model.getReasoningSummaries` + pub const SESSION_MODEL_GETREASONINGSUMMARIES: &str = "session.model.getReasoningSummaries"; /// `session.model.list` pub const SESSION_MODEL_LIST: &str = "session.model.list"; /// `session.mode.get` @@ -724,6 +890,8 @@ pub mod rpc_methods { pub const SESSION_PLUGINS_MARKETPLACES_REFRESH: &str = "session.plugins.marketplaces.refresh"; /// `session.plugins.reload` pub const SESSION_PLUGINS_RELOAD: &str = "session.plugins.reload"; + /// `session.plugins.retryManaged` + pub const SESSION_PLUGINS_RETRYMANAGED: &str = "session.plugins.retryManaged"; /// `session.provider.getEndpoint` pub const SESSION_PROVIDER_GETENDPOINT: &str = "session.provider.getEndpoint"; /// `session.provider.add` @@ -1759,6 +1927,67 @@ pub struct AccountAllUsers { pub token: Option, } +/// Credential-free authentication identity safe to expose to hosts and user interfaces. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AuthIdentity { + /// Snapshot of the authenticated user's Copilot subscription info, if known + #[serde(skip_serializing_if = "Option::is_none")] + pub copilot_user: Option, + /// Name of the environment variable that supplied the credential, when applicable + #[serde(skip_serializing_if = "Option::is_none")] + pub env_var: Option, + /// Authentication host + pub host: String, + /// Authenticated login, when available + #[serde(skip_serializing_if = "Option::is_none")] + pub login: Option, + /// Opaque SDK GitHub credential registration backing this identity. Routing metadata only; never a credential. + #[serde(skip_serializing_if = "Option::is_none")] + pub registration_id: Option, + /// Authentication type + pub r#type: AuthInfoType, +} + +/// The authentication identity whose shared wording should be rendered. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct AccountDescribeAuthInfoRequest { + /// Identity to describe. + pub identity: AuthIdentity, +} + +/// The wording the runtime uses for an authentication identity. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct AccountDescribeAuthInfoResult { + /// One-line description of the identity, suitable after a verb such as "Switched to". + pub description: String, + /// Sign-in banner announcing the authenticated user. + pub message: String, +} + /// Current authentication state /// ///
@@ -1778,6 +2007,42 @@ pub struct AccountGetCurrentAuthResult { pub auth_info: Option, } +/// The authentication identity whose gist support should be reported. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct AccountGetGistSupportRequest { + /// Identity to evaluate. + pub identity: AuthIdentity, +} + +/// Whether the identity can create gists, and why not when it cannot. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct AccountGetGistSupportResult { + /// Machine-readable reason category when unsupported. + #[serde(skip_serializing_if = "Option::is_none")] + pub kind: Option, + /// Human-readable explanation to show where the gist action would have been. + #[serde(skip_serializing_if = "Option::is_none")] + pub reason: Option, + /// True when the identity can create gists. + pub supported: bool, +} + /// Optional opaque account selection or compatibility GitHub token used to look up quota. /// ///
@@ -1842,6 +2107,55 @@ pub struct AccountGetQuotaResult { pub quota_snapshots: HashMap, } +/// The environment variable supplying a classic personal access token, when one is set. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct AccountGetTokenEnvVarResult { + /// Variable name, for example `GH_TOKEN`. Absent when no classic token is present in the environment. + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, +} + +/// A path to resolve against the GitHub host an identity is signed in to. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct AccountGetWebUrlRequest { + /// Identity whose host the path resolves against. Omit to resolve against the public github.com host. + #[serde(skip_serializing_if = "Option::is_none")] + pub identity: Option, + /// Host-relative path, without a leading slash, for example `settings/copilot/features`. + pub path: String, +} + +/// An absolute URL on the identity's GitHub host. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct AccountGetWebUrlResult { + /// Absolute URL for the requested path. + pub url: String, +} + /// Credentials to validate and store. Omit login to resolve the authenticated user from the token. /// ///
@@ -2039,6 +2353,9 @@ pub struct AccountsSetRequest { pub struct AccountStatus { /// Whether this is the active account. pub active: bool, + /// Human-readable credential source used to authenticate this account, such as `gh` or `GITHUB_TOKEN`. + #[serde(skip_serializing_if = "Option::is_none")] + pub auth_source: Option, /// Opaque id of the account this one was derived from (e.g. an EMU account's base Entra identity); absent for a root account. Matches the base identity account's selectionId, forming the derivation edge. #[serde(skip_serializing_if = "Option::is_none")] pub derived_from: Option, @@ -2107,6 +2424,9 @@ pub struct AgentInfo { pub disable_model_invocation: Option, /// Human-readable display name pub display_name: String, + /// Declarative hook actions keyed by event name, executed only while this agent is active. + #[serde(skip_serializing_if = "Option::is_none")] + pub hooks: Option>>, /// Stable identifier for selection. For most agents this is the same as `name`; for plugin/builtin agents it may differ. Always populated; defaults to `name` when no distinct id was assigned. pub id: String, /// MCP server configurations attached to this agent, keyed by server name. Server config shape mirrors the MCP `mcpServers` schema. @@ -2477,6 +2797,63 @@ pub struct AgentsDiscoverRequest { pub project_paths: Option>, } +/// Extensible [`AgentsDiscoverRequest`], including inputs added after it was published. +/// +/// Required inputs are [`AgentsDiscoverOptions::new`] arguments; optional inputs have fluent setters. +/// Input-only: it serialises to the flat wire request and is not deserialisable. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AgentsDiscoverOptions { + #[serde(flatten)] + legacy: AgentsDiscoverRequest, + #[serde(skip_serializing_if = "Option::is_none")] + agent_directories: Option>, +} + +impl AgentsDiscoverOptions { + /// Creates options with the required inputs. + pub fn new() -> Self { + Self { + legacy: AgentsDiscoverRequest { + project_paths: None, + exclude_host_agents: None, + }, + agent_directories: None, + } + } + + /// Optional list of project directory paths to scan for project-scoped agents. When omitted or empty, only user/plugin/remote-independent agents are returned (no project scan). + pub fn project_paths(mut self, value: Vec) -> Self { + self.legacy.project_paths = Some(value); + self + } + + /// When true, omit the host's agents (the user-level agent directory and all plugin agents), leaving only project and remote agents. For multitenant deployments. + pub fn exclude_host_agents(mut self, value: bool) -> Self { + self.legacy.exclude_host_agents = Some(value); + self + } + + /// Explicit discovery-only agent roots, separate from customAgents and filesystem grants. Available without ambient discovery, subject to managed/source disables. Relative paths resolve against the request's effective working directory when supplied. Null and omission are equivalent; an empty array contributes no explicit roots. + pub fn agent_directories(mut self, value: Vec) -> Self { + self.agent_directories = Some(value); + self + } +} + +impl Default for AgentsDiscoverOptions { + fn default() -> Self { + Self::new() + } +} + /// Name of the custom agent to select for subsequent turns. /// ///
@@ -2677,6 +3054,63 @@ pub struct AgentsGetDiscoveryPathsRequest { pub project_paths: Option>, } +/// Extensible [`AgentsGetDiscoveryPathsRequest`], including inputs added after it was published. +/// +/// Required inputs are [`AgentsGetDiscoveryPathsOptions::new`] arguments; optional inputs have fluent setters. +/// Input-only: it serialises to the flat wire request and is not deserialisable. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AgentsGetDiscoveryPathsOptions { + #[serde(flatten)] + legacy: AgentsGetDiscoveryPathsRequest, + #[serde(skip_serializing_if = "Option::is_none")] + agent_directories: Option>, +} + +impl AgentsGetDiscoveryPathsOptions { + /// Creates options with the required inputs. + pub fn new() -> Self { + Self { + legacy: AgentsGetDiscoveryPathsRequest { + project_paths: None, + exclude_host_agents: None, + }, + agent_directories: None, + } + } + + /// Optional list of project directory paths. When omitted or empty, only the user-level directory is returned. + pub fn project_paths(mut self, value: Vec) -> Self { + self.legacy.project_paths = Some(value); + self + } + + /// When true, omit the host's user-level agent directory, leaving only project directories. For multitenant deployments (mirrors `discover`'s `excludeHostAgents`). + pub fn exclude_host_agents(mut self, value: bool) -> Self { + self.legacy.exclude_host_agents = Some(value); + self + } + + /// Explicit discovery-only agent roots to enumerate, including configured missing directories. Relative paths resolve against the request's effective working directory when supplied. These are not filesystem grants or preferred creation targets. Null and omission are equivalent. + pub fn agent_directories(mut self, value: Vec) -> Self { + self.agent_directories = Some(value); + self + } +} + +impl Default for AgentsGetDiscoveryPathsOptions { + fn default() -> Self { + Self::new() + } +} + /// Blob attachment with inline base64-encoded data /// ///
@@ -3215,35 +3649,6 @@ pub struct AuthEnumerateValueProviders { pub kind: AuthEnumerateValueProvidersKind, } -/// Credential-free authentication identity safe to expose to hosts and user interfaces. -/// -///
-/// -/// **Experimental.** This type is part of an experimental wire-protocol surface -/// and may change or be removed in future SDK or CLI releases. -/// -///
-#[derive(Debug, Clone, Default, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct AuthIdentity { - /// Snapshot of the authenticated user's Copilot subscription info, if known - #[serde(skip_serializing_if = "Option::is_none")] - pub copilot_user: Option, - /// Name of the environment variable that supplied the credential, when applicable - #[serde(skip_serializing_if = "Option::is_none")] - pub env_var: Option, - /// Authentication host - pub host: String, - /// Authenticated login, when available - #[serde(skip_serializing_if = "Option::is_none")] - pub login: Option, - /// Opaque SDK GitHub credential registration backing this identity. Routing metadata only; never a credential. - #[serde(skip_serializing_if = "Option::is_none")] - pub registration_id: Option, - /// Authentication type - pub r#type: AuthInfoType, -} - /// Credential-free identity metadata. /// ///
@@ -8200,6 +8605,128 @@ pub struct ExternalToolTextResultForLlmContentText { pub r#type: ExternalToolTextResultForLlmContentTextType, } +/// The declared shape of one feature flag, independent of any caller population. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct FeatureFlagMetadata { + /// The population the flag is declared for before any environment or user-settings override is applied. + #[doc(hidden)] + pub(crate) availability: FeatureFlagAvailability, + /// Whether the flag participates in the CAPI sanity suite. + pub capi_sanity: bool, + /// Text shown to a user choosing whether to turn the flag on in experimental mode. Absent when the flag is not user-facing. + #[serde(skip_serializing_if = "Option::is_none")] + pub experimental_description: Option, + /// The online-experiment (ExP) flag this maps to, when the flag is assigned by an experiment rather than declared statically. + #[serde(skip_serializing_if = "Option::is_none")] + pub exp_flag: Option, + /// Environment variable that overrides this flag's experiment assignment locally in the experiment service, not in `featureFlags.resolve`. Present exactly when `expFlag` is, and derived from it, so a caller reads a local override without reproducing the naming rule. + #[serde(skip_serializing_if = "Option::is_none")] + pub exp_flag_env_var: Option, + /// The flag's name, matching the key it is listed under. + pub name: String, +} + +/// Every feature flag this runtime declares, with the shape each one was declared with. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct FeatureFlagsMetadataResult { + /// Declared feature flags keyed by flag name. + #[doc(hidden)] + pub(crate) flags: HashMap, +} + +/// Caller population and overrides for static feature-flag resolution, independent of experiment assignments and active runtime state. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct FeatureFlagsResolveRequest { + /// Resolve against this already-computed flag set rather than each flag's declared availability. Keyed by flag name. + #[serde(skip_serializing_if = "Option::is_none")] + pub base_flags: Option>, + /// User settings to apply, including `enabledFeatureFlags`, legacy `feature_flags.enabled`, and supported convenience settings. + #[serde(skip_serializing_if = "Option::is_none")] + pub config: Option, + /// Environment for direct feature-flag overrides and `COPILOT_CLI_ENABLED_FEATURE_FLAGS`. Does not apply `COPILOT_EXP_*` experiment assignments. Defaults to this runtime process's own environment. + #[serde(skip_serializing_if = "Option::is_none")] + pub environment: Option>, + /// Flags forced to a value after every other layer, keyed by flag name. + #[serde(skip_serializing_if = "Option::is_none")] + pub flag_overrides: Option>, + /// Whether the caller is in an experimental population. + pub is_experimental: bool, + /// Whether the caller is GitHub or Microsoft staff. + pub is_staff: bool, + /// GitHub-staff membership. Omit when it was never recorded: a legacy staff account then counts as both staff populations and keeps every staff feature. + #[serde(skip_serializing_if = "Option::is_none")] + pub is_staff_github: Option, + /// Microsoft-staff membership. Omitted the same way as `isStaffGitHub`. + #[serde(skip_serializing_if = "Option::is_none")] + pub is_staff_microsoft: Option, + /// Whether the caller belongs to the team population used by feature availability. + pub is_team: bool, +} + +/// Caller population and startup inputs to resolve named online-experiment gates for. Carries no account: the runtime resolves the assignment for the authentication it already holds. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct FeatureFlagsResolveStartupGatesRequest { + /// User settings document whose `featureFlags` block is layered on top of the resolved set. + #[serde(skip_serializing_if = "Option::is_none")] + pub config: Option, + /// Environment to read flag overrides from. Defaults to this runtime process's own environment. + #[serde(skip_serializing_if = "Option::is_none")] + pub environment: Option>, + /// When this installation first launched, in milliseconds since the Unix epoch. Experiments that segment on installation age read it. Omit when the first launch was never recorded. + #[serde(skip_serializing_if = "Option::is_none")] + pub first_launch_at_millis: Option, + /// The feature flags to resolve. A name this runtime does not declare is reported as an error rather than as a disabled flag. + pub flags: Vec, + /// Whether the caller is in an experimental population. + pub is_experimental: bool, + /// Whether the caller is GitHub or Microsoft staff. + pub is_staff: bool, + /// GitHub-staff membership. Omit when it was never recorded: a legacy staff account then counts as both staff populations and keeps every staff feature. + #[serde(skip_serializing_if = "Option::is_none")] + pub is_staff_github: Option, + /// Microsoft-staff membership. Omitted the same way as `isStaffGitHub`. + #[serde(skip_serializing_if = "Option::is_none")] + pub is_staff_microsoft: Option, + /// Whether the caller belongs to a team plan. + pub is_team: bool, + /// Whether the runtime may refresh the cached assignment over the network without an active session. The refresh never blocks this call: the answer comes from the cache either way, so a refresh only affects a later launch. + pub refresh_without_session: bool, + /// Whether streamer mode is on, which withholds the populations an experiment would otherwise segment on. + pub streamer_mode: bool, +} + /// Parameters for starting fleet orchestration: an optional user prompt combined with the fleet instructions, plus the send options forwarded to the resulting turn. /// ///
@@ -8286,6 +8813,54 @@ pub struct FolderTrustCheckResult { pub trusted: bool, } +/// A folder to check against a persisted trusted-folders list, and the configuration directory holding that list. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct FolderTrustIsTrustedParams { + /// Configuration directory to read the trusted-folders list from. Omit it to read the default location; supply it when the host has redirected its configuration, or the check answers from a list the host is not using. + #[serde(skip_serializing_if = "Option::is_none")] + pub config_dir: Option, + /// Folder to check. A folder counts as trusted when it is itself listed, when an ancestor is listed, or when its git root is listed. + pub path: String, +} + +/// The verdict of the `forceLoginOrgs` enterprise sign-in pin for one credential. A managed device may pin Copilot to an approved set of GitHub organizations so a developer cannot sign in with a personal — or another enterprise's — account and escape every control that keys off the signed-in identity. A host must refuse to proceed when `allowed` is false; the remaining fields explain the refusal rather than qualifying it. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ForceLoginOrgsDecision { + /// Whether the client may proceed. True whenever no pin is in force, so an unmanaged host is never blocked. + pub allowed: bool, + /// A stable kebab-case reason code when refused: `org-not-approved`, `unverifiable-credential`, `policy-undetermined`, `policy-invalid`, `not-signed-in`, or `membership-unknown`. Omitted when allowed. Treat an unrecognized code as a refusal rather than mapping it to an allow: a code this host has not been taught about still denied the credential. + #[serde(skip_serializing_if = "Option::is_none")] + pub denial: Option, + /// Whether a pin applies at all. Distinguishes "allowed because unmanaged" from "allowed because the account is approved", which a diagnostics surface needs and which also decides whether a bring-your-own-key provider may serve inference. + pub enforced: bool, + /// The approved organization the account matched, when allowed under a pin. + #[serde(skip_serializing_if = "Option::is_none")] + pub matched_org: Option, + /// A developer-facing explanation of the refusal. Omitted when allowed. + #[serde(skip_serializing_if = "Option::is_none")] + pub message: Option, + /// Error-level detail from managed-policy discovery — which file failed to load, which field failed validation. Carried on the decision rather than left to the log because a refusal can exit the process before a log sink exists, which would discard the only actionable diagnostic a misconfigured managed device produces. + pub policy_diagnostics: Vec, + /// The approved organization logins, as configured. Enterprise configuration rather than user data, so it is safe to name in a message or a log line. + pub required_orgs: Vec, +} + /// The remote the checked-out branch tracks. /// ///
@@ -8768,9 +9343,6 @@ pub(crate) struct LoggedInUser { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub(crate) struct GlobalStateLoadResult { - /// Whether the user has answered the prompt suggesting they install the desktop app. - #[serde(skip_serializing_if = "Option::is_none")] - pub app_install_nudge_responded: Option, /// Whether the app tip has been shown. #[serde(skip_serializing_if = "Option::is_none")] pub app_tip_shown: Option, @@ -9279,6 +9851,63 @@ pub struct HooksDiscoverRequest { pub project_paths: Option>, } +/// Extensible [`HooksDiscoverRequest`], including inputs added after it was published. +/// +/// Required inputs are [`HooksDiscoverOptions::new`] arguments; optional inputs have fluent setters. +/// Input-only: it serialises to the flat wire request and is not deserialisable. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct HooksDiscoverOptions { + #[serde(flatten)] + legacy: HooksDiscoverRequest, + #[serde(skip_serializing_if = "Option::is_none")] + hook_directories: Option>, +} + +impl HooksDiscoverOptions { + /// Creates options with the required inputs. + pub fn new() -> Self { + Self { + legacy: HooksDiscoverRequest { + project_paths: None, + exclude_host_hooks: None, + }, + hook_directories: None, + } + } + + /// Optional project directory paths whose trusted repository and project-expanded plugin hooks should be discovered. When omitted or empty, user, managed-policy, and globally enabled installed or explicit plugin hooks are returned without project expansion. + pub fn project_paths(mut self, value: Vec) -> Self { + self.legacy.project_paths = Some(value); + self + } + + /// When true, omit host-owned user and plugin hook rows and their diagnostics. Managed-policy hooks and trusted repository hooks remain visible, and host disabledHooks still contribute to each remaining row's effective enabled state. This filters sources rather than simulating a host with no settings. + pub fn exclude_host_hooks(mut self, value: bool) -> Self { + self.legacy.exclude_host_hooks = Some(value); + self + } + + /// Explicit discovery-only file-hook roots, separate from callback hooks and filesystem grants. Available without ambient discovery or repository file-hook enablement, subject to managed/hook disables. Relative paths resolve against the request's effective working directory when supplied. Null and omission are equivalent; an empty array contributes no explicit roots. + pub fn hook_directories(mut self, value: Vec) -> Self { + self.hook_directories = Some(value); + self + } +} + +impl Default for HooksDiscoverOptions { + fn default() -> Self { + Self::new() + } +} + /// Server-discovered hook actions and partial-load diagnostics from user, repository, plugin, and managed-policy sources. Concrete sessions may include additional session-specific hook sources. /// ///
@@ -10627,6 +11256,36 @@ pub struct InstructionDiscoveryPathList { pub paths: Vec, } +/// Explicit discovery arguments for one location, supplied by the caller rather than derived from a session. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct InstructionsDiscoverRepoSourcesRequest { + /// Extra directories to scan alongside instruction directories configured in the runtime environment. + #[serde(skip_serializing_if = "Option::is_none")] + pub additional_dirs: Option>, + /// Copilot configuration directory for user-level sources. An omitted or empty value uses the server's configuration home. + #[serde(skip_serializing_if = "Option::is_none")] + pub config_dir: Option, + /// Working directory whose instruction sources should also be included. Omission adds no separate working-directory scope. + #[serde(skip_serializing_if = "Option::is_none")] + pub cwd_path: Option, + /// When true, also discover instruction files nested below the location. + #[serde(skip_serializing_if = "Option::is_none")] + pub enable_child_instructions: Option, + /// When true, include the sources a command-line host reads. Hosts that are not the CLI omit it. + #[serde(skip_serializing_if = "Option::is_none")] + pub is_cli: Option, + /// Repository location to discover. An empty string selects no repository scope; cwdPath can still supply working-directory sources. + pub location: String, +} + /// Optional project paths to include in instruction discovery. /// ///
@@ -10646,6 +11305,63 @@ pub struct InstructionsDiscoverRequest { pub project_paths: Option>, } +/// Extensible [`InstructionsDiscoverRequest`], including inputs added after it was published. +/// +/// Required inputs are [`InstructionsDiscoverOptions::new`] arguments; optional inputs have fluent setters. +/// Input-only: it serialises to the flat wire request and is not deserialisable. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct InstructionsDiscoverOptions { + #[serde(flatten)] + legacy: InstructionsDiscoverRequest, + #[serde(skip_serializing_if = "Option::is_none")] + instruction_directories: Option>, +} + +impl InstructionsDiscoverOptions { + /// Creates options with the required inputs. + pub fn new() -> Self { + Self { + legacy: InstructionsDiscoverRequest { + project_paths: None, + exclude_host_instructions: None, + }, + instruction_directories: None, + } + } + + /// Optional list of project directory paths to scan for repository/working-directory instruction sources. When omitted or empty, only user-level and plugin instruction sources are returned (no project scan). + pub fn project_paths(mut self, value: Vec) -> Self { + self.legacy.project_paths = Some(value); + self + } + + /// When true, omit the host's instruction sources (user/home-level files and plugin rules), leaving only repository and working-directory sources. For multitenant deployments. + pub fn exclude_host_instructions(mut self, value: bool) -> Self { + self.legacy.exclude_host_instructions = Some(value); + self + } + + /// Explicit discovery-only instruction roots using existing SDK instruction formats, not filesystem grants. Available without ambient discovery, subject to instruction/managed disables. Relative paths resolve against the request's effective working directory when supplied. Null and omission are equivalent; an empty array contributes no explicit roots. + pub fn instruction_directories(mut self, value: Vec) -> Self { + self.instruction_directories = Some(value); + self + } +} + +impl Default for InstructionsDiscoverOptions { + fn default() -> Self { + Self::new() + } +} + /// Optional project paths to include when enumerating instruction discovery targets. /// ///
@@ -10665,6 +11381,63 @@ pub struct InstructionsGetDiscoveryPathsRequest { pub project_paths: Option>, } +/// Extensible [`InstructionsGetDiscoveryPathsRequest`], including inputs added after it was published. +/// +/// Required inputs are [`InstructionsGetDiscoveryPathsOptions::new`] arguments; optional inputs have fluent setters. +/// Input-only: it serialises to the flat wire request and is not deserialisable. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct InstructionsGetDiscoveryPathsOptions { + #[serde(flatten)] + legacy: InstructionsGetDiscoveryPathsRequest, + #[serde(skip_serializing_if = "Option::is_none")] + instruction_directories: Option>, +} + +impl InstructionsGetDiscoveryPathsOptions { + /// Creates options with the required inputs. + pub fn new() -> Self { + Self { + legacy: InstructionsGetDiscoveryPathsRequest { + project_paths: None, + exclude_host_instructions: None, + }, + instruction_directories: None, + } + } + + /// Optional list of project directory paths. When omitted or empty, only the user-level targets are returned. + pub fn project_paths(mut self, value: Vec) -> Self { + self.legacy.project_paths = Some(value); + self + } + + /// When true, omit the host's user-level instruction targets, leaving only repository targets. For multitenant deployments (mirrors `discover`'s `excludeHostInstructions`). + pub fn exclude_host_instructions(mut self, value: bool) -> Self { + self.legacy.exclude_host_instructions = Some(value); + self + } + + /// Explicit discovery-only instruction roots to enumerate, including configured missing directories. Relative paths resolve against the request's effective working directory when supplied. These are not filesystem grants or preferred creation targets. Null and omission are equivalent. + pub fn instruction_directories(mut self, value: Vec) -> Self { + self.instruction_directories = Some(value); + self + } +} + +impl Default for InstructionsGetDiscoveryPathsOptions { + fn default() -> Self { + Self::new() + } +} + /// Loaded instruction source for a session, including path, content, category, location, applicability, and optional description. /// ///
@@ -10717,6 +11490,21 @@ pub struct InstructionsGetSourcesResult { pub sources: Vec, } +/// The prompt the init command runs. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct InstructionsInitPromptResult { + /// Prompt text authoring repository instructions. + pub prompt: String, +} + /// Parameters for interrupting the main agent turn. /// ///
@@ -11015,6 +11803,21 @@ pub struct LocalSessionMetadataValue { pub summary: Option, } +/// Which stored location permissions were dropped because their directory is gone. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct LocationPermissionsCleanupResult { + /// Location keys whose grants were removed. Empty when every stored location still exists. + pub removed_location_keys: Vec, +} + /// Message text, optional severity level, persistence flag, optional follow-up URL, and optional tip. /// ///
@@ -11108,6 +11911,92 @@ pub struct ManagedMcpServerConfig { pub url: String, } +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ManagedPermissionOperation { + /// Operation to evaluate. Currently only url is supported. + pub kind: ManagedPermissionOperationKind, + /// Absolute HTTP(S) URL with a host. Invalid URLs reject the entire evaluation batch. + pub url: String, +} + +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ManagedPermissionEvaluation { + /// Original input operation associated with this verdict. + pub operation: ManagedPermissionOperation, + /// Managed-policy decision only; never bypasses user permissions or other security controls. + pub verdict: ManagedPermissionVerdict, +} + +/// Managed policy context and ordered operations for pure, sessionless evaluation. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ManagedPermissionsEvaluateRequest { + /// Trusted, reusable managed permission policy snapshot; no policy is discovered during evaluation. + pub context: ManagedPermissionsContext, + /// Operations to evaluate, preserving input order and duplicates. An empty batch is valid. + pub operations: Vec, +} + +/// Ordered managed permission verdicts and the supplied policy's fail-closed posture. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ManagedPermissionsEvaluateResult { + /// Managed permission policy could not be determined; every operation is denied. + pub fail_closed: bool, + /// One verdict per input operation, in the same order, including duplicates. + pub results: Vec, +} + +/// Retry outcome for one plugin required by managed settings. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ManagedPluginRetryEntry { + /// Why the plugin was not prepared. Present when `status` is `failed` or `deferred`. + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, + /// Plugin spec in "plugin@marketplace" form. + pub spec: String, + /// What the retry did for this plugin. + pub status: ManagedPluginRetryStatus, +} + /// Lock state and provenance of one managed setting. /// ///
@@ -11128,6 +12017,39 @@ pub struct ManagedSettingMeta { pub source: String, } +/// A user settings document and the effective managed policy whose controlled entries should be stamped onto it. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ManagedSettingsApplyControlsParams { + /// The effective managed policy, as composed by `managedSettings.resolveEffectiveValue`. Omit it on an unmanaged host: the settings then come back unchanged. + #[serde(skip_serializing_if = "Option::is_none")] + pub managed: Option, + /// The user settings document to stamp. Entries the managed layer does not name are returned exactly as supplied. + pub settings: serde_json::Value, +} + +/// The user settings with the managed layer's controlled entries stamped on. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ManagedSettingsApplyControlsResult { + /// The resulting settings document. + pub settings_json: serde_json::Value, +} + /// One candidate channel; absent settings represents a channel that delivered no document. /// ///
@@ -11234,20 +12156,23 @@ pub struct ManagedSettingsMeta { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ManagedSettingsResolvedData { - /// Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers, and forced on when `failClosed` is true. + /// Whether an explicit enterprise policy restriction disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers; an unresolved policy does not force this on by itself. pub bypass_permissions_disabled: bool, /// Whether a session-local permissions layer injected by the SDK host was present #[serde(skip_serializing_if = "Option::is_none")] pub client_managed: Option, /// Whether an actual device MDM/plist/registry/file managed-settings layer was present pub device_managed: bool, - /// Whether managed policy could not be determined (e.g. a failed server fetch) and the session fell back to the fail-closed restriction. When true, restrictions such as disabling bypass-permissions are enforced even though `settings` may be absent. + /// Whether managed policy could not be determined (e.g. a failed server fetch) and unresolved-policy safeguards remain active. This does not by itself disable bypass-permissions; `bypassPermissionsDisabled` reports only an explicit policy restriction. pub fail_closed: bool, /// The setting keys under enterprise management in the effective managed settings (e.g. `model`, `enabledPlugins`, `permissions`). Empty when no managed settings are in force. pub managed_keys: Vec, /// Whether at least two managed sources supplied permission allowlists, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. #[serde(skip_serializing_if = "Option::is_none")] pub permissions_allow_intersected: Option, + /// Reusable retained managed permission policy for managedSettings.permissions.evaluate, including source composition and session-local injection. Provided by live session snapshots; absent in older events. Refresh the context when managed policy changes. + #[serde(skip_serializing_if = "Option::is_none")] + pub permissions_context: Option, /// Whether the policy-helper managed-settings layer was present. The policy helper is the weakest channel: it fills keys no enterprise source set and can never replace one. #[serde(skip_serializing_if = "Option::is_none")] pub policy_helper_managed: Option, @@ -11313,6 +12238,272 @@ pub struct ManagedSettingsComposeResult { pub values: Option, } +/// Inputs describing the host and account an ingestion should resolve managed policy for. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ManagedSettingsIngestParams { + /// The authenticated identity to resolve policy for. Omitted before sign-in, in which case no server layer is fetched. + #[serde(skip_serializing_if = "Option::is_none")] + pub auth_info: Option, + /// Whether to ignore a cached server policy when reading, forcing the resolution to come from a live fetch. + #[serde(skip_serializing_if = "Option::is_none")] + pub bypass_cache_read: Option, + /// Name of the embedding client, reported to the managed-settings service as `client_id` so it can refuse to deliver a policy this client is too old to enforce. Omit it and the runtime reports a generic identity; the runtime version sent alongside is the gate that actually matters. + #[serde(skip_serializing_if = "Option::is_none")] + pub client_name: Option, + /// A device layer supplied by the host instead of discovered from the machine. Pair it with `hasDeviceResponse` so an explicit absence is distinguishable from an omitted override. + #[serde(skip_serializing_if = "Option::is_none")] + pub device_response: Option, + /// The host's environment variables, read for cache location and policy overrides. Supplied by the host rather than read from the runtime process so a command that selected its own configuration home is honored. + pub environment: serde_json::Value, + /// Whether `deviceResponse` is an override at all. True with a null `deviceResponse` asserts the device layer is empty; false discovers it from the machine. + #[serde(skip_serializing_if = "Option::is_none")] + pub has_device_response: Option, + /// The host's home directory, used to resolve cache and managed-file locations. + pub home_directory: String, + /// The host's platform, in Node's `process.platform` spelling, used to locate platform-specific cache and policy paths. + pub platform: String, + /// Whether a configured platform policy helper may be run to produce an enforced helper layer. + #[serde(skip_serializing_if = "Option::is_none")] + pub policy_helper_enabled: Option, + /// The policy-helper invocation reason. Defaults to sessionStart. + #[doc(hidden)] + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) policy_helper_reason: Option, + /// Directory the policy helper runs in when this ingestion is not owned by a registered session. A host resolving policy before its session exists must pass this, or a directory-sensitive helper judges the wrong repository. + #[serde(skip_serializing_if = "Option::is_none")] + pub policy_helper_working_directory: Option, + /// Whether to run ingestion. Omit or set false to return an empty result without discovering or fetching policy. + #[serde(skip_serializing_if = "Option::is_none")] + pub self_fetch: Option, + /// How this ingestion may use the on-disk server-policy cache. Defaults to the runtime's own policy when omitted. + #[doc(hidden)] + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) server_policy_cache_mode: Option, + /// The session whose credential store and working directory the policy helper should use. Omit it when no session exists yet and the process-wide store applies. + #[serde(skip_serializing_if = "Option::is_none")] + pub session_id: Option, + /// User agent to send with the policy fetch. + pub user_agent: String, +} + +/// One diagnostic emitted while ingesting managed settings. Returned rather than logged by the runtime so the host decides where ingestion diagnostics go, which matters because an ingestion runs before a session exists to carry a log sink. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ManagedSettingsLogMessage { + /// Severity of this diagnostic. + #[doc(hidden)] + pub(crate) level: ManagedSettingsLogLevel, + /// Human-readable diagnostic text. + pub message: String, +} + +/// The managed policy layers an ingestion resolved, together with the per-layer outcome flags a host needs to decide how much to trust the result. The flags are reported rather than folded into the settings because a host must be able to distinguish an unmanaged device from a managed device whose policy could not be confirmed, and apply its fail-closed gates in the second case. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ManagedSettingsIngestResult { + /// The device layer could not be read, so it is unconfirmed rather than known-absent. + pub device_load_failed: bool, + /// The device layer discovered from the machine, when one exists. + #[serde(skip_serializing_if = "Option::is_none")] + pub device_response: Option, + /// The sandbox floor contributed by the device layer. + #[serde(skip_serializing_if = "Option::is_none")] + pub device_sandbox_floor: Option, + /// Whether the device layer's sandbox intent could not be determined, so a consumer must not read its absence as permission to run unsandboxed. + pub device_sandbox_undetermined: bool, + /// A device-managed policy is known to exist. + pub device_source_found: bool, + /// Diagnostics emitted while resolving the layers, in the order they occurred. + #[doc(hidden)] + pub(crate) log_messages: Vec, + /// The effective managed permissions layer, when the resolved policy carries one. + #[serde(skip_serializing_if = "Option::is_none")] + pub permissions: Option, + /// Which channel registered the policy helper that produced the helper layer. + #[doc(hidden)] + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) policy_helper_authority: Option, + /// A policy helper is configured for this host. + pub policy_helper_configured: bool, + /// The policy helper's failure must be treated as fail-closed rather than ignored. + pub policy_helper_fail_closed: bool, + /// The policy helper was configured but could not be run or its output could not be read. + pub policy_helper_load_failed: bool, + /// The layer produced by the platform policy helper, when one ran. + #[serde(skip_serializing_if = "Option::is_none")] + pub policy_helper_response: Option, + /// A live fetch failed but a recent cached entry was served in its place, so `serverFetchFailed` stays false. Lets a consumer distinguish a clean fetch from a cache-backed fallback. + pub server_cache_fallback: bool, + /// Minimum client version requested by a successful response. Advisory only; the delivered policy remains active. + #[serde(skip_serializing_if = "Option::is_none")] + pub server_client_update_requested: Option, + /// The server refused to deliver a policy because this client is too old to enforce it, carrying the user-facing sentence describing the refusal. No retry will help; the remedy is a client update. Gates that exist only because of managed policy should deny on this rather than falling back to their unmanaged default. + #[serde(skip_serializing_if = "Option::is_none")] + pub server_client_update_required: Option, + /// The error that made the server fetch fail, when one did. + #[serde(skip_serializing_if = "Option::is_none")] + pub server_fetch_error: Option, + /// The server fetch failed and no cached entry stood in for it, so the server layer is unconfirmed and fail-closed gates apply. + pub server_fetch_failed: bool, + /// The server fetch failed while a policy demanded a fresh confirmation, which is the strict posture an enterprise opted into rather than an ordinary transient failure. + pub server_forced_refresh_failure: bool, + /// The synthesized fail-closed permissions layer applied in place of a policy a refused client may not enforce. + #[serde(skip_serializing_if = "Option::is_none")] + pub server_permissions_floor: Option, + /// How the server layer was resolved. + #[doc(hidden)] + pub(crate) server_resolution: ManagedSettingsServerPolicyResolution, + /// The account's server-delivered policy, when one was resolved. + #[serde(skip_serializing_if = "Option::is_none")] + pub server_response: Option, + /// The server fetch failed in a way a later retry could succeed at, as opposed to a refusal that no retry will change. + pub server_retryable_failure: bool, + /// The sandbox object from a cached server policy that a forced refresh declined to serve in full. Folded back into the effective sandbox layer so an outage during a forced refresh still synthesizes the fail-closed sandbox floor from the administrator's known intent. + #[serde(skip_serializing_if = "Option::is_none")] + pub server_sandbox_floor: Option, + /// A server-managed policy is known to exist even though `serverResponse` is intentionally unset. Consumers should OR this with the presence of `serverResponse` when deciding whether the device is managed, so a fresh start during an outage still applies the fail-closed gates instead of treating the device as unmanaged. + pub server_source_found: bool, + /// No usable token was available, so no server fetch was attempted. + pub server_token_unavailable: bool, + /// The effective managed settings composed from every resolved layer. Omitted when no layer set anything, so an unmanaged host locks nothing. + #[serde(skip_serializing_if = "Option::is_none")] + pub settings: Option, +} + +/// Inputs describing the host and account whose managed remote-control policy should be resolved. Mirrors `managedSettings.ingest`, and deliberately carries no retention decision: reading this gate never becomes this runtime process's startup policy. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ManagedSettingsLoadRemoteControlParams { + /// The authenticated identity to resolve policy for. Omitted before sign-in, in which case no server layer is fetched. + #[serde(skip_serializing_if = "Option::is_none")] + pub auth_info: Option, + /// Name of the embedding client, reported to the managed-settings service as `client_id` so it can refuse to deliver a policy this client is too old to enforce. That refusal is exactly what turns into an explicit `disabled` below, so a host that omits this field forfeits the protection. + #[serde(skip_serializing_if = "Option::is_none")] + pub client_name: Option, + /// A device layer supplied by the host instead of discovered from the machine. Pair it with `hasDeviceResponse` so an explicit absence is distinguishable from an omitted override. + #[serde(skip_serializing_if = "Option::is_none")] + pub device_response: Option, + /// The host's environment variables, read for cache location and policy overrides. + pub environment: serde_json::Value, + /// Whether `deviceResponse` is an override at all. True with a null `deviceResponse` asserts the device layer is empty; false discovers it from the machine. + #[serde(skip_serializing_if = "Option::is_none")] + pub has_device_response: Option, + /// The host's home directory, used to resolve cache and managed-file locations. + pub home_directory: String, + /// The host's platform, in Node's `process.platform` spelling, used to locate platform-specific cache and policy paths. + pub platform: String, + /// Whether a configured platform policy helper may be run to produce an enforced helper layer. + #[serde(skip_serializing_if = "Option::is_none")] + pub policy_helper_enabled: Option, + /// The policy-helper invocation reason. Defaults to sessionStart. + #[doc(hidden)] + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) policy_helper_reason: Option, + /// Whether to run ingestion. Omit or set false to return an empty result without discovering or fetching policy. + #[serde(skip_serializing_if = "Option::is_none")] + pub self_fetch: Option, + /// User agent to send with the policy fetch. + pub user_agent: String, +} + +/// The managed remote-control policy in force for an account, if any. An absent policy means the account is unmanaged for this capability, which a host reads as allowed — so absence here is a positive statement that no policy applies, never a stand-in for a resolution that failed. A resolution that cannot be completed is rejected instead. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ManagedSettingsLoadRemoteControlResult { + /// The account's `remoteControl` policy document. Absent when no managed policy governs remote control for this account. Reported as an explicit disabled policy, rather than as absence, when the service refused to deliver a policy this client is too old to enforce. + #[serde(skip_serializing_if = "Option::is_none")] + pub remote_control: Option, +} + +/// The client-supplied managed settings to validate and normalize. Omitting them is valid and yields no policy. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ManagedSettingsParseClientValueParams { + /// The managed settings a client is supplying for a session it is about to open. Omit them when the client is imposing no policy of its own. + #[serde(skip_serializing_if = "Option::is_none")] + pub settings: Option, +} + +/// The accepted managed settings, reduced to the keys a client is allowed to set. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ManagedSettingsParseClientValueResult { + /// The normalized settings. Omitted when the client supplied none. An accepted document that sets nothing comes back as an empty object, which is distinct from omission. + #[serde(skip_serializing_if = "Option::is_none")] + pub settings_json: Option, +} + +/// The device-managed settings layer, with the sandbox floor and the load outcome a host needs to compose it into an effective policy. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ManagedSettingsReadDeviceResult { + /// Whether discovery or validation failed. A host must treat this as "policy unknown" rather than "no policy": absent settings with this set false mean the device genuinely has none. + pub load_failed: bool, + /// Sandbox floor read from the discovered document before validation, so a document that is otherwise rejected can still raise the floor it declared. + #[serde(skip_serializing_if = "Option::is_none")] + pub sandbox_floor: Option, + /// Validated, canonical device-managed settings. Omitted when no device policy was discovered, when the discovered document failed validation, or when it validated to an empty object — an empty document carries no policy, and reporting it as present would outrank layers that do. + #[serde(skip_serializing_if = "Option::is_none")] + pub settings: Option, +} + /// Validated device-managed settings discovered before a session exists. /// ///
@@ -11332,6 +12523,115 @@ pub struct ManagedSettingsReadResult { pub settings_json: Option, } +/// The bypass-permissions state a host holds today, the account now signed in, and what the managed-policy resolution said. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ManagedSettingsResolveBypassDisableTransitionParams { + /// The account signed in now. A `managed` disable pinned to a different account is lifted. + pub active_identity: String, + /// The account a `managed` disable is pinned to. Omit it when the current source is not `managed`; it is ignored for the other sources. + #[serde(skip_serializing_if = "Option::is_none")] + pub current_managed_identity: Option, + /// Which layer currently disables bypass mode. Omit it when nothing does. + #[doc(hidden)] + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) current_source: Option, + /// What the resolution said about bypass mode. Send `unknown` rather than omitting the field when there is no verdict. + #[doc(hidden)] + pub(crate) policy: ManagedSettingsBypassPolicyResolution, +} + +/// The bypass-permissions state a host should hold after the resolution, and what it must do to get there. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ManagedSettingsResolveBypassDisableTransitionResult { + /// The account the returned `managed` source is pinned to. Store it alongside `source`; the next call needs it to decide whether an account switch lifts the disable. + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_identity: Option, + /// The value to store in the host's own bypass-disabled flag. Omitted means leave it exactly as it is, which is not the same as being told to store `false`. + #[serde(skip_serializing_if = "Option::is_none")] + pub set_disabled_state: Option, + /// Which layer disables bypass mode after this resolution. Omitted when none does, which is what a lifted disable looks like. + #[doc(hidden)] + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) source: Option, + /// Whether a bypass-permissions session already running must be turned off. True only when this resolution newly disables bypass mode, so a host that is already compliant is not interrupted. + pub suppress_runtime_allow_all: bool, +} + +/// The credential and environment to evaluate the device-channel sign-in pin against. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ManagedSettingsResolveDeviceForceLoginOrgsGateParams { + /// The authenticated identity to judge. Omitted when signed out, which is the case the device channel exists to serve: it can refuse a first sign-in before any credential exists. + #[serde(skip_serializing_if = "Option::is_none")] + pub auth_info: Option, + /// The host's environment variables. Read for the variables that would make an identity's membership snapshot forgeable, so a credential whose provenance cannot be trusted is refused rather than accepted. + pub environment: serde_json::Value, +} + +/// The managed-settings layers to compose into an effective policy. Every layer is optional; omitting all of them yields no effective policy. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ManagedSettingsResolveEffectiveValueParams { + /// The device sandbox floor, folded into the device layer under `sandbox` before composition. + #[serde(skip_serializing_if = "Option::is_none")] + pub device_sandbox_floor: Option, + /// The device layer: native MDM and managed-file settings describing the machine rather than the account. + #[serde(skip_serializing_if = "Option::is_none")] + pub mdm: Option, + /// The policy-helper layer, an enforced channel sourced from a platform policy helper process. + #[serde(skip_serializing_if = "Option::is_none")] + pub policy_helper: Option, + /// The account's server-delivered org policy, as returned by a managed-settings fetch. Omitted before sign-in or when the fetch has not landed. + #[serde(skip_serializing_if = "Option::is_none")] + pub server: Option, +} + +/// The effective managed policy composed from the supplied layers. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ManagedSettingsResolveEffectiveValueResult { + /// The composed effective managed settings. Omitted when no layer sets anything, so an unmanaged host locks nothing. Carries `sandboxAdvisoryFloorDeclined: true` when an advisory server force-on was dropped because this host cannot run the sandbox. + #[serde(skip_serializing_if = "Option::is_none")] + pub settings_json: Option, +} + /// Optional opaque account selection or GitHub token whose managed settings are resolved. /// ///
@@ -11378,6 +12678,9 @@ pub struct ManagedSettingsResolveResult { /// Per-key lock state and provenance for the entries in `values`, using the same key names. #[serde(skip_serializing_if = "Option::is_none")] pub meta: Option, + /// Reusable source-composed permission policy for managedSettings.permissions.evaluate. Unlike resolved.settings.permissions, this retains every source's allowlist and default-prompt semantics. Refresh through resolve when account/device policy changes. Absent on runtimes that do not support permission evaluation. + #[serde(skip_serializing_if = "Option::is_none")] + pub permissions_context: Option, /// Effective managed settings from the device and account (server) channels, in the same shape as `session.managedSettings.get`, excluding session-local injection. pub resolved: ManagedSettingsResolvedData, /// Typed effective values of managed settings, keyed like the managed-settings schema and already resolved across channels, with the `{ "overridable": ... }` wrapper removed. Present when policy sets at least one typed key. Keys not typed here are available in `resolved.settings`. @@ -11385,6 +12688,172 @@ pub struct ManagedSettingsResolveResult { pub values: Option, } +/// Inputs describing the host and account a startup policy resolution should run for. Mirrors `managedSettings.ingest`, and adds the decision of whether the resolved policy is retained for this runtime process. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ManagedSettingsResolveStartupPolicyParams { + /// The authenticated identity to resolve policy for. Omitted before sign-in, in which case no server layer is fetched and the sign-in pin is evaluated against no credential. + #[serde(skip_serializing_if = "Option::is_none")] + pub auth_info: Option, + /// Whether to ignore a cached server policy when reading, forcing the resolution to come from a live fetch. + #[serde(skip_serializing_if = "Option::is_none")] + pub bypass_cache_read: Option, + /// Name of the embedding client, reported to the managed-settings service as `client_id` so it can refuse to deliver a policy this client is too old to enforce. + #[serde(skip_serializing_if = "Option::is_none")] + pub client_name: Option, + /// A device layer supplied by the host instead of discovered from the machine. Pair it with `hasDeviceResponse` so an explicit absence is distinguishable from an omitted override. + #[serde(skip_serializing_if = "Option::is_none")] + pub device_response: Option, + /// The host's environment variables, read for cache location, policy overrides, and the variables that would make a membership snapshot forgeable. + pub environment: serde_json::Value, + /// Whether `deviceResponse` is an override at all. True with a null `deviceResponse` asserts the device layer is empty; false discovers it from the machine. + #[serde(skip_serializing_if = "Option::is_none")] + pub has_device_response: Option, + /// The host's home directory, used to resolve cache and managed-file locations. + pub home_directory: String, + /// The host's platform, in Node's `process.platform` spelling, used to locate platform-specific cache and policy paths. + pub platform: String, + /// Whether a configured platform policy helper may be run to produce an enforced helper layer. + #[serde(skip_serializing_if = "Option::is_none")] + pub policy_helper_enabled: Option, + /// The policy-helper invocation reason. Defaults to sessionStart. + #[doc(hidden)] + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) policy_helper_reason: Option, + /// Directory the policy helper runs in when this resolution is not owned by a registered session. A host resolving policy before its session exists must pass this, or a directory-sensitive helper judges the wrong repository. + #[serde(skip_serializing_if = "Option::is_none")] + pub policy_helper_working_directory: Option, + /// Whether to retain the resolved policy as this runtime process's startup policy, so later work in the process enforces it. Set false to resolve without side effects, which is what a protocol host wants before session creation: its sessions apply the complete policy through the normal lifecycle, and retaining here would let a readiness credential affect unrelated sessions. The returned verdict is identical either way — this decides only whether the process keeps it. + pub retain_and_apply: bool, + /// Whether to run ingestion. Omit or set false to return an empty result without discovering or fetching policy. + #[serde(skip_serializing_if = "Option::is_none")] + pub self_fetch: Option, + /// How this resolution may use the on-disk server-policy cache. Defaults to the runtime's own policy when omitted. Rejected when combined with `bypassCacheRead`, because the two express the same decision and accepting both would leave the caller unsure which one the runtime honored. + #[doc(hidden)] + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) server_policy_cache_mode: Option, + /// The session whose credential store, working directory and client kind the resolution should use. Omit it when no session exists yet and the process-wide store applies. A resolution whose session names a credential other than `authInfo` is returned but not retained, so a stale sign-in cannot apply policy for an account that has already been switched away from. + #[serde(skip_serializing_if = "Option::is_none")] + pub session_id: Option, + /// User agent to send with the policy fetch. + pub user_agent: String, +} + +/// The startup policy a resolution composed, the per-layer outcome flags a host needs to decide how much to trust it, and the enterprise sign-in verdict for the credential it ran against. The flags are reported rather than folded into the settings because a host must distinguish an unmanaged device from a managed device whose policy could not be confirmed, and apply its fail-closed gates in the second case. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ManagedSettingsResolveStartupPolicyResult { + /// Advisory minimum client version from a successful server response. + #[serde(skip_serializing_if = "Option::is_none")] + pub client_update_requested: Option, + /// The server refused to deliver a policy because this client is too old to enforce it, carrying the user-facing sentence describing the refusal. No retry will help; the remedy is a client update. + #[serde(skip_serializing_if = "Option::is_none")] + pub client_update_required: Option, + /// The device layer could not be read, so it is unconfirmed rather than known-absent. + pub device_load_failed: bool, + /// The device layer discovered from the machine, when one exists. + #[serde(skip_serializing_if = "Option::is_none")] + pub device_response: Option, + /// The sandbox floor contributed by the device layer. + #[serde(skip_serializing_if = "Option::is_none")] + pub device_sandbox_floor: Option, + /// The enterprise sign-in verdict for the credential this resolution ran against, evaluated over the effective policy so a server-delivered pin is honored alongside a device one. Computed on every resolution, including one that was not retained, so a host that declines to apply policy still cannot proceed past a pin. + #[doc(hidden)] + pub(crate) force_login_orgs: ForceLoginOrgsDecision, + /// A managed policy is known to apply to this host, from a discovered source or a resolved document. + pub managed_policy_present: bool, + /// The permissions layer specifically could not be confirmed, so permission gates apply their fail-closed posture. + pub permission_policy_undetermined: bool, + /// The policy helper was configured but could not be run, or its output could not be read. + pub policy_helper_load_failed: bool, + /// The layer produced by the platform policy helper, when one ran. + #[serde(skip_serializing_if = "Option::is_none")] + pub policy_helper_response: Option, + /// Some layer of the policy could not be confirmed, so its absence must not be read as permission. Folds in a failed server fetch as well as an unreadable device layer. + pub policy_undetermined: bool, + /// The sandbox was enabled because policy could not be confirmed rather than because an administrator asked for it. Lets a host explain a sandbox the user did not configure. + pub sandbox_enabled_by_undetermined_policy: bool, + /// The effective sandbox floor composed across every layer, which a host must apply rather than its own default. + #[serde(skip_serializing_if = "Option::is_none")] + pub sandbox_floor: Option, + /// The server fetch failed and no cached entry stood in for it, so the server layer is unconfirmed and fail-closed gates apply. + pub server_fetch_failed: bool, + /// How the current runtime resolved the server layer. + #[doc(hidden)] + pub(crate) server_resolution: ManagedSettingsServerPolicyResolution, + /// The account's server-delivered policy, when one was resolved. + #[serde(skip_serializing_if = "Option::is_none")] + pub server_response: Option, + /// Telemetry describing which layers answered and how, for the host to report alongside its own startup events. + pub telemetry: serde_json::Value, + /// Warnings raised while resolving, in the order they occurred. + pub warnings: Vec, +} + +/// The managed layers to retain as this process's startup policy, with the outcome of fetching each one. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ManagedSettingsRetainPolicyParams { + /// Whether the device load failed. Report it honestly, for the same reason as `serverFetchFailed`. + pub device_load_failed: bool, + /// The device/MDM policy layer. + #[serde(skip_serializing_if = "Option::is_none")] + pub device_response: Option, + /// Sandbox floor declared by the device layer, carried separately so a document that failed validation can still raise the floor. + #[serde(skip_serializing_if = "Option::is_none")] + pub device_sandbox_floor: Option, + /// Whether the device sandbox intent is unreadable independently of its other settings. + pub device_sandbox_undetermined: bool, + /// Whether a device policy source demonstrably exists. This, not a read error, is the evidence a device is managed, so a fail-closed policy gates on it. + pub device_source_found: bool, + /// Whether any managed policy source exists, independently of fetch failures. + pub managed_policy_present: bool, + /// Whether permission policy is undetermined. Sandbox-only uncertainty does not imply this flag. + pub permission_policy_undetermined: bool, + /// The policy helper's response: the third managed layer that a process owning its startup policy resolves. It is retained with the other two so that every ambient read in this process, including the mutation guards and the governed marketplace allowlist that the `plugins.*` handlers derive, sees the whole policy the process fetched. A session-owning process omits it, because it resolves a helper per session against that session's working directory. + #[serde(skip_serializing_if = "Option::is_none")] + pub policy_helper_response: Option, + /// Preserve the retained server layer during a device-only resolution. + pub preserve_server_state: bool, + /// Whether uncertainty alone enabled the shared sandbox floor. + pub sandbox_enabled_by_undetermined_policy: bool, + /// Whether the shared sandbox policy is undetermined. Diagnostic errors alone do not imply this flag. + pub sandbox_floor_undetermined: bool, + /// Whether the server fetch failed. Report it honestly: true keeps the already-retained server layer rather than replacing it with an absent one. + pub server_fetch_failed: bool, + /// Whether a required fresh server-policy confirmation failed. + pub server_forced_refresh_failure: bool, + /// The resolved server policy. A known source can lack a response after a forced-refresh failure. + #[serde(skip_serializing_if = "Option::is_none")] + pub server_response: Option, + /// Whether the server policy source exists, even when its response is unavailable. + pub server_source_found: bool, + /// Shared server/device sandbox floor, excluding session-local helper policy. + #[serde(skip_serializing_if = "Option::is_none")] + pub shared_sandbox_floor: Option, +} + /// The authoring JSON schema for managed settings recognized by this runtime. /// ///
@@ -12902,6 +14371,21 @@ pub struct McpIsServerRunningResult { pub running: bool, } +/// Names of the MCP servers the runtime supplies itself. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct McpListBuiltinServerNamesResult { + /// Every built-in server name. A host disabling the built-ins wholesale suppresses exactly this set. + pub names: Vec, +} + /// Server name whose tool list should be returned. /// ///
@@ -13926,6 +15410,91 @@ pub(crate) struct McpRegisterExternalClientRequest { pub(crate) transport: serde_json::Value, } +/// Registry search to abandon. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct McpRegistryCancelRequest { + /// Request ID from `mcp.registry.allocateRequestId` that the search uses. + pub request_id: i64, +} + +/// Whether the cancel reached a live search. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct McpRegistryCancelResult { + /// True when the cancel stopped a running search. False for unknown or reclaimed IDs, completed or canceled searches, and unused reservations. The cancel releases an unused reservation. + pub canceled: bool, +} + +/// Request id naming a cancellable registry search. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct McpRegistryRequestIdResult { + /// Request ID for `mcp.registry.search` and `mcp.registry.cancel`. It serves one search. Allocation can reclaim unused IDs at the 1,024-ID reservation limit. + pub request_id: i64, +} + +/// Registry search terms, the credential to search under, and the request id that makes the search cancellable. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct McpRegistrySearchRequest { + /// The credential the search runs under, carried opaquely. Hosts usually send credential-free `AuthIdentity`; a `token` identity can be resolved only when it embeds a token, while `env` and `gh-cli` identities can use a token embedded in the request first. + pub auth_info: serde_json::Value, + /// Maximum number of servers to return. + pub limit: i64, + /// Free-text query. Omitted or empty asks the registry for its top servers rather than searching. A value that is not a string is refused. + #[serde(skip_serializing_if = "Option::is_none")] + pub query: Option, + /// Repository used for the policy lookup, as `owner/name`. The policy selects the registry URL and whether the user token goes to the registry. The registry receives this repository only when the policy entry lists it as required context. A value that is not a string is refused. + #[serde(skip_serializing_if = "Option::is_none")] + pub repository: Option, + /// Request ID from `mcp.registry.allocateRequestId`. The search refuses unknown, reclaimed, or canceled IDs and IDs that another search or request already uses. + pub request_id: i64, +} + +/// Servers selected from the registry response. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct McpRegistrySearchResult { + /// The server objects, carried opaquely. The runtime follows pages, keeps the newest entry per server name, cuts the list to `limit`, and sorts an empty-query result by GitHub stars. Each server object remains unchanged because the registry owns that shape. + pub servers: serde_json::Value, +} + /// In-process MCP reload configuration. /// ///
@@ -14746,6 +16315,43 @@ pub struct McpSetEnvValueModeResult { pub mode: McpSetEnvValueModeDetails, } +/// The session tool filters that decide whether the session still has a shell to run `gh` with. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct McpShouldExcludeGitHubToolsRequest { + /// The session's tool allowlist, when it set one. Omitted means the session constrains nothing this way. + #[serde(skip_serializing_if = "Option::is_none")] + pub available_tools: Option>, + /// The session's tool denylist, when it set one. Omitted means the session constrains nothing this way. + #[serde(skip_serializing_if = "Option::is_none")] + pub excluded_tools: Option>, + /// How the allowlist and denylist combine when both are set. Omitted means the default every session gets, so a caller that never chose a precedence is answered as its sessions behave. + #[serde(skip_serializing_if = "Option::is_none")] + pub tool_filter_precedence: Option, +} + +/// Whether the gh-replaceable GitHub MCP tools may be clipped for the session described by the request. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct McpShouldExcludeGitHubToolsResult { + /// True only when both halves hold: the session's filters still reach the platform shell tool, and the host actually has the `gh` those tools would be replaced by. Feed it straight back as the `excludeGhReplaceableTools` build option. + pub exclude_gh_replaceable_tools: bool, +} + /// Server name and optional configuration for an individual MCP server start. Omit `config` for a config-free start-by-name of an already-configured server. /// ///
@@ -15758,6 +17364,55 @@ pub(crate) struct ModelClearStartupSeedResult { pub cleared: bool, } +/// Host presentation state the reasoning-summaries policy depends on. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ModelGetReasoningSummariesRequest { + /// The host's persisted `showReasoning` user setting, omitted when unset. + #[serde(skip_serializing_if = "Option::is_none")] + pub persisted_show_reasoning: Option, + /// Whether the host is currently showing reasoning summaries. Defaults to false. + #[serde(skip_serializing_if = "Option::is_none")] + pub summaries_visible: Option, +} + +/// Reasoning-summaries presentation policy for the session's current model. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ModelGetReasoningSummariesResult { + /// Whether the supplied `persistedShowReasoning` setting only restates the model default. This reports a redundancy, not a standing instruction to delete the setting: it stays true for as long as the setting and the model default agree, including for a user who later chose that value deliberately. It exists for a one-time migration of a legacy setting written before the model default made it redundant, so a host that acts on it owns the cleanup and must gate it behind its own persisted `done` marker, delete only a setting whose value still matches the one it read, and never repeat the deletion on a later launch. Under an off-by-default reasoning-summaries experiment an explicit `showReasoning: true` opt-in also reads as redundant on a summary-producing model, so an ungated host would keep deleting that opt-in and hide summaries again on every launch. + pub clear_redundant_setting: bool, + /// Whether the session's reasoning-summaries experiment assignment has settled. False means this read does not wait for a pending assignment and reports the experiment-derived fields conservatively: `showHiddenHint` is false and `visibility` falls back to `persistedShowReasoning`, or is omitted when none was supplied. A host that wants the assigned answer re-reads until this is true. + pub experiment_settled: bool, + /// Whether the selected model shows reasoning summaries by default. + pub model_default_shows_summaries: bool, + /// False until the session has a model catalog and a selected model. This read never resolves the catalog itself, and the runtime emits no readiness notification, so repeating this call alone leaves `ready` false indefinitely on a session that has neither listed models nor run a turn. `session.model.list` is the prerequisite and the signal: it resolves and caches the catalog for the session, so a successful return means the next read is ready. The exception is a session that resolves models without a Copilot catalog, that is a BYOK registry or an alternate provider: `session.model.list` returns an empty list without caching a catalog, so `ready` stays false and this policy is unavailable there. While false, every model-policy field is false and `visibility` is omitted; `experimentSettled` is independent of readiness and still reports the assignment state. + pub ready: bool, + /// Whether turns on this session produce reasoning summaries, honouring an explicit session reasoning-summary override. + pub session_shows_summaries: bool, + /// Whether to show the hint that reasoning summaries are hidden. Always false while `experimentSettled` is false. + pub show_hidden_hint: bool, + /// Whether the selected model emits reasoning headers. + pub show_reasoning_headers: bool, + /// Default reasoning visibility, and only meaningful once `ready` is true: false when the session produces no summaries, else the persisted setting, else the experiment's default. While `ready` is true it is omitted only when all three are unavailable, that is when the session produces summaries, no `persistedShowReasoning` was supplied, and `experimentSettled` is false. While `ready` is false there is no model policy to report and this field is always omitted, so an absent value alone does not imply a pending experiment. + #[serde(skip_serializing_if = "Option::is_none")] + pub visibility: Option, +} + /// List of Copilot models available to the resolved user, including capabilities and billing metadata. /// ///
@@ -18213,6 +19868,41 @@ pub struct PermissionsConfigureResult { pub success: bool, } +/// The verbatim managed `permissions` slice to interpret. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct PermissionsEvaluateManagedPolicyParams { + /// The `permissions` object from a managed settings layer, for example `{"disableBypassPermissionsMode":"disable"}`. Omitted or null means no managed policy applies. + #[serde(skip_serializing_if = "Option::is_none")] + pub permissions: Option, +} + +/// The policy questions a host asks before granting elevated permissions. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PermissionsEvaluateManagedPolicyResult { + /// True when managed policy forbids bypass-permissions mode, so allow-all style flags must not take effect. + pub bypass_permissions_disabled: bool, + /// True when the managed layer supplies permission rules that the session must enforce. + pub permission_rules_active: bool, + /// True when a managed policy exists but this build could not determine its meaning. Hosts fail closed on it rather than assuming permission was granted. + pub policy_undetermined: bool, +} + /// Indicates whether the operation succeeded. /// ///
@@ -18540,6 +20230,44 @@ pub struct PermissionUrlsSetUnrestrictedModeParams { pub enabled: bool, } +/// A complete JSON snapshot to write under the runtime's per-path file lock. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PersistenceWriteRawJsonLockedRequest { + /// The serialized JSON document to write. It replaces the file's contents entirely; nothing is merged. + pub data_json: String, + /// Comment lines to emit above the document, for a format that tolerates them. Omit for strict JSON. + #[serde(skip_serializing_if = "Option::is_none")] + pub file_header: Option, + /// Absolute path of the file to replace. Parent directories are created when missing. + pub path: String, +} + +/// The outcome of a locked snapshot write. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PersistenceWriteRawJsonLockedResult { + /// Why the write failed. Absent when `success` is true. A lock the runtime could not acquire in time reports here rather than raising. + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, + /// Whether the file now holds the requested contents. + pub success: bool, +} + /// Optional message to echo back to the caller. /// ///
@@ -20865,6 +22593,110 @@ pub struct RemoteSessionRepository { pub owner: String, } +/// One atomic edit of an allowed top-level repository setting. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct RepoSettingsEditRequest { + /// One canonical top-level repository setting that the allowlist permits. + pub key: String, + /// Set a scalar, add or remove a string list item, or remove the setting. The handler rejects a missing or incompatible value. + #[doc(hidden)] + pub(crate) operation: SettingsEditOperation, + /// Absolute path of the repository root. + pub repo_root: String, + /// Shared or local repository settings file. + #[doc(hidden)] + pub(crate) scope: RepoSettingsEditScope, + /// Required boolean, string or number for set, required string for add-item and remove-item, and forbidden for remove. The runtime rejects all other JSON values. + #[serde(skip_serializing_if = "Option::is_none")] + pub value: Option, +} + +/// Which repository settings file to read. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct RepoSettingsReadRawRequest { + /// Absolute path of the repository root. + pub repo_root: String, + /// Use repo for shared settings or local for settings.local.json. Other scopes are rejected. + pub scope: String, +} + +/// Parsed repository settings without normalization or layering. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct RepoSettingsReadRawResult { + /// Stored JSON values after the existing JSONC parser and prototype-safety checks. Empty when the file is missing, blank, or invalid. + pub config: serde_json::Value, + /// True when the file exists but did not parse, so `config` is empty for that reason rather than because nothing is set. + pub invalid: bool, +} + +/// The complete set of repository-overridable settings to store. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct RepoSettingsWriteAllRequest { + /// Absolute path of the repository root. + pub repo_root: String, + /// Use repo for shared settings or local for settings.local.json. Other scopes are rejected. + pub scope: String, + /// Object of allowed settings to store. Missing allowlisted keys are removed. Other input keys are ignored, while unknown stored keys are preserved. + pub settings: serde_json::Value, +} + +/// One key to write or remove in a repository settings file. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct RepoSettingsWriteKeyRequest { + /// True to remove the key instead of writing it. + #[serde(skip_serializing_if = "Option::is_none")] + pub delete: Option, + /// Canonical top-level key permitted by the repository settings allowlist. + pub key: String, + /// Absolute path of the repository root. + pub repo_root: String, + /// Use repo for shared settings or local for settings.local.json. Other scopes are rejected. + pub scope: String, + /// Value to validate and write. Required unless delete is true; a null value is validated, not treated as deletion. + #[serde(skip_serializing_if = "Option::is_none")] + pub value: Option, +} + /// Provider-native structured output format. JSON Schema is forwarded without rewriting or validating the schema or the generated output. /// ///
@@ -20882,6 +22714,21 @@ pub struct ResponseFormat { pub r#type: ResponseFormatType, } +/// The stored sandbox settings to resolve against the defaults. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SandboxApplyDefaultsRequest { + /// The sandbox settings as stored in the settings file. An empty object resolves to the defaults alone. + pub stored: serde_json::Value, +} + /// Credential-injection capability flags applied while the sandbox is enabled. For the same capability independent of sandboxing, and matched to the credential's GitHub host, see `shell.credentials`; the two are additive. /// ///
@@ -21125,6 +22972,38 @@ pub struct SandboxConfig { pub user_policy: Option, } +/// A possible secret-bearing environment variable and optional locally suggested HTTPS injection hosts. This is a draft for user review, not an active grant. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SandboxCredentialSuggestion { + /// Environment variable name. The secret value is never returned. + pub name: String, + /// HTTPS hostnames suggested by a local known-provider mapping. An empty list requires the user to supply injection hosts before adding a masking entry. These suggestions do not allow network access. + pub suggested_inject_hosts: Vec, +} + +/// Possible unconfigured secrets in the sandbox shell environment, sorted by variable name, with no secret values. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SandboxCredentialSuggestionsResult { + /// Candidates for user review. Empty when sandboxing is disabled or no unconfigured candidates are present. + pub suggestions: Vec, +} + /// Request to disable sandboxing for the current session while resolving an active sandbox-bypass permission prompt. /// ///
@@ -21160,6 +23039,71 @@ pub struct SandboxDisableForSessionResult { pub success: bool, } +/// The documentation URL a host cites when it declines or downgrades a sandbox setting. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SandboxDocsUrlResult { + /// Documentation URL describing the sandbox and the settings that control it. + pub url: String, +} + +/// A sandbox config and the per-feature routing flag to resolve it for. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct SandboxEffectiveForOptionalParams { + /// The routing flag naming the feature being launched, such as `sandboxMcpServers` or `sandboxLspServers`. + pub routing_flag: String, + /// The session's resolved `sandbox` slice. Omitted when the host has none, which yields a disabled sandbox rather than an error. + #[serde(skip_serializing_if = "Option::is_none")] + pub sandbox: Option, +} + +/// The sandbox a named feature actually launches under. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct SandboxEffectiveForOptionalResult { + /// The effective sandbox config for the requested feature, or a disabled sandbox when the feature is routed out of it. + pub sandbox_json: serde_json::Value, +} + +/// One slash command, as typed, to test against the enforcement-block allowlist. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct SandboxEnforcementCommandAllowedParams { + /// The command's arguments, in order. Pass an empty array for a bare command: several entries in the allowlist admit the bare form and refuse every argument. + pub args: Vec, + /// The command name as the user typed it, with or without its leading slash. + pub command_name: String, +} + /// Managed sandbox enforcement state for a session. /// ///
@@ -21180,6 +23124,59 @@ pub struct SandboxEnforcementStatus { pub required: bool, } +/// One file-picker suggestion offered up for the sandbox filesystem check. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct SandboxPickerCandidate { + /// Whether the suggestion is a directory. A directory the policy does not itself allow may still be needed as a waypoint to an allowed subtree. + pub is_directory: bool, + /// Absolute path the suggestion would attach. + pub path: String, +} + +/// A batch of file-picker candidates to check against a prepared policy. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct SandboxFilterPickerCandidatesParams { + /// The candidates to check, in the order the answers are returned. + #[doc(hidden)] + pub(crate) candidates: Vec, + /// The session's working directory, matching the one the policy was prepared from. + pub cwd: String, + /// The policy returned by `sandbox.preparePickerPolicy`. Omitted keeps every candidate. + #[serde(skip_serializing_if = "Option::is_none")] + pub sandbox: Option, +} + +/// Per-candidate allow decisions, positionally aligned with the request. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct SandboxFilterPickerCandidatesResult { + /// One decision per candidate, in the same order. Always the same length as the request's `candidates`. + pub allowed: Vec, +} + /// Request to accept the sandbox path grant offered on an active sandbox escalation permission prompt. /// ///
@@ -21253,6 +23250,40 @@ pub struct SandboxHostSupport { pub supported: bool, } +/// The working directory and sandbox settings to build a file-picker policy from. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct SandboxPreparePickerPolicyParams { + /// The session's working directory. Git discovery and grant canonicalization are resolved against it. + pub cwd: String, + /// The session's resolved `sandbox` slice. Omitted, or disabled, yields no policy and every candidate is kept. + #[serde(skip_serializing_if = "Option::is_none")] + pub sandbox: Option, +} + +/// The prepared file-picker policy, or none when the sandbox is disabled. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct SandboxPreparePickerPolicyResult { + /// The prepared policy to pass back to `sandbox.filterPickerCandidates`. Absent when the sandbox is disabled or absent, in which case filtering keeps every candidate. + #[serde(skip_serializing_if = "Option::is_none")] + pub sandbox_json: Option, +} + /// Result of creating the persistent certificate authority of the sandbox credential proxy. /// ///
@@ -21304,6 +23335,138 @@ pub struct SandboxProxyCaStatus { pub state: SandboxProxyCaState, } +/// The stored sandbox proxy password, if one is present. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SandboxProxySecretGetResult { + /// The stored password. Absent when no entry exists in either the keychain or the on-disk fallback store. + #[serde(skip_serializing_if = "Option::is_none")] + pub value: Option, +} + +/// Where this runtime keeps the sandbox proxy password. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SandboxProxySecretLocation { + /// Absolute path of the credential store holding the sandbox proxy password. The proxy password belongs to no MCP server, so it is kept in its own directory rather than in the MCP secret store. + pub config_dir: String, + /// Identifier of the proxy password entry within that store. Derived from the configuration directory, so two Copilot homes on one machine never share an entry. Use it to recognize a `${secret:…}` reference in stored settings as this runtime's own proxy password. + pub secret_id: String, +} + +/// The sandbox proxy password to store. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SandboxProxySecretSaveRequest { + /// The password to store for the sandbox proxy. + pub value: String, +} + +/// Where the sandbox proxy password was stored. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SandboxProxySecretSaveResult { + /// Whether the OS keychain accepted the value. False means it was written to the on-disk fallback store instead, which is still readable by `sandbox.proxySecret.get`. + pub stored_in_keychain: bool, +} + +/// The user's sandbox settings and the managed sandbox floor to raise them to. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct SandboxResolveFlooredConfigParams { + /// The effective managed `sandbox` slice acting as a floor. Omitted when no administrator policy applies, in which case the user's own settings stand unchanged. + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_sandbox: Option, + /// The user's own `sandbox` settings slice. Omitted when the user has configured none, which still yields the managed floor rather than a disabled sandbox. + #[serde(skip_serializing_if = "Option::is_none")] + pub user_sandbox: Option, +} + +/// The user's sandbox settings raised to the managed floor. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SandboxResolveFlooredConfigResult { + /// The floored sandbox config, normalized through the declared sandbox shape so only declared fields in a fixed order are present. + pub sandbox_json: serde_json::Value, +} + +/// The managed sandbox policy in force, which selects which remedy the notice offers. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct SandboxUnsupportedStartupNoticeParams { + /// True when the sandbox stayed on only because the managed policy could not be determined. Distinguishes a policy that positively enforces the sandbox from one that merely failed to resolve, which is the difference between telling the user to contact an administrator and telling them to wait. Defaults to false. + #[serde(skip_serializing_if = "Option::is_none")] + pub enabled_by_undetermined_policy: Option, + /// The managed `sandbox` slice, when a policy supplies one. The notice treats the sandbox as enforced only when this both enables it and sets `failIfUnavailable`, because a managed sandbox that tolerates being unavailable still leaves the user able to turn it off. + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_sandbox: Option, +} + +/// The startup warning, absent when the machine can run a sandbox. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct SandboxUnsupportedStartupNoticeResult { + /// The user-facing warning, carrying the platform remedy and the recovery the policy in force actually allows. Absent when this machine supports a sandbox, so a host can call unconditionally and print only what it gets. + #[serde(skip_serializing_if = "Option::is_none")] + pub notice: Option, +} + /// Register an absolute-time scheduled prompt. /// ///
@@ -23344,6 +25507,9 @@ pub struct SessionOpenOptions { /// Runtime context discriminator for agent filtering. #[serde(skip_serializing_if = "Option::is_none")] pub agent_context: Option, + /// Explicit discovery-only agent roots, separate from customAgents and filesystem grants. Available without enableConfigDiscovery, subject to managed/source disables. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. + #[serde(skip_serializing_if = "Option::is_none")] + pub agent_directories: Option>, /// Whether to include instructions from every MCP server in the system prompt instead of only allowlisted servers. #[serde(skip_serializing_if = "Option::is_none")] pub allow_all_mcp_server_instructions: Option, @@ -23424,7 +25590,7 @@ pub struct SessionOpenOptions { /// Whether shell-script safety heuristics are enabled. #[serde(skip_serializing_if = "Option::is_none")] pub enable_script_safety: Option, - /// Whether skill loading is enabled. When omitted, an SDK skill provider enables skills by default. + /// Whether skill loading is enabled. When omitted, explicit SDK skill roots or an SDK skill provider enable skills independently of enableConfigDiscovery. #[serde(skip_serializing_if = "Option::is_none")] pub enable_skills: Option, /// Whether model responses stream as delta events. @@ -23466,6 +25632,9 @@ pub struct SessionOpenOptions { #[doc(hidden)] #[serde(skip_serializing_if = "Option::is_none")] pub(crate) has_skill_provider: Option, + /// Explicit discovery-only file-hook roots, separate from callback hooks and filesystem grants. Available without enableConfigDiscovery or enableFileHooks, which controls repository-origin hooks only; hook/managed disables still apply. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. + #[serde(skip_serializing_if = "Option::is_none")] + pub hook_directories: Option>, /// Skill scan directories and descendants excluded from discovery. Supports `~`-relative paths. #[serde(skip_serializing_if = "Option::is_none")] pub ignored_skills_locations: Option>, @@ -23478,6 +25647,9 @@ pub struct SessionOpenOptions { /// Installed plugins visible to the session. #[serde(skip_serializing_if = "Option::is_none")] pub installed_plugins: Option>, + /// Explicit discovery-only instruction roots using existing SDK instruction formats, not filesystem grants. Available without enableConfigDiscovery, subject to instruction/managed disables. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. + #[serde(skip_serializing_if = "Option::is_none")] + pub instruction_directories: Option>, /// Stable integration identifier for analytics. #[serde(skip_serializing_if = "Option::is_none")] pub integration_id: Option, @@ -23595,7 +25767,7 @@ pub struct SessionOpenOptions { /// PowerShell process flags applied to built-in and user-requested shell commands. #[serde(skip_serializing_if = "Option::is_none")] pub shell_process_flags: Option>, - /// Additional directories to search for skills. + /// Explicit discovery-only skill roots, not filesystem grants. Available without enableConfigDiscovery, subject to skill/managed disables. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. #[serde(skip_serializing_if = "Option::is_none")] pub skill_directories: Option>, /// Whether to skip custom instruction sources. @@ -23884,6 +26056,37 @@ pub struct SessionPluginsInstallRequest { pub source: String, } +/// Plugins required by the session's managed settings to install or update again. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SessionPluginsRetryManagedRequest { + /// "plugin@marketplace" specs to retry. Omit to retry every plugin the session's managed settings require. + #[serde(skip_serializing_if = "Option::is_none")] + pub plugins: Option>, +} + +/// Per-plugin outcomes of retrying the plugins required by the session's managed settings. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SessionPluginsRetryManagedResult { + /// One entry per requested plugin, in request order. When no plugins were requested, one entry per plugin the managed settings require. + pub plugins: Vec, +} + /// Outcome of the prune operation: deleted IDs, dry-run candidates, skipped IDs, total bytes freed, and the dry-run flag. /// ///
@@ -25186,6 +27389,9 @@ pub struct SessionUpdateOptionsParams { /// Runtime context discriminator (e.g., `cli`, `actions`). #[serde(skip_serializing_if = "Option::is_none")] pub agent_context: Option, + /// Explicit discovery-only agent roots, separate from customAgents and filesystem grants. Available without enableConfigDiscovery, subject to managed/source disables. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. + #[serde(skip_serializing_if = "Option::is_none")] + pub agent_directories: Option>, /// Whether to include instructions from every MCP server in the system prompt instead of only allowlisted servers. #[serde(skip_serializing_if = "Option::is_none")] pub allow_all_mcp_server_instructions: Option, @@ -25222,7 +27428,7 @@ pub struct SessionUpdateOptionsParams { /// Skill IDs that should be excluded from this session. #[serde(skip_serializing_if = "Option::is_none")] pub disabled_skills: Option>, - /// Whether to enable loading of `.github/hooks/` filesystem hooks. Separate from the SDK callback hook mechanism. + /// Whether to enable repository-origin file hooks from `.github/hooks/` and trusted repository-configured roots. Does not control explicit hookDirectories or SDK callback hooks. #[serde(skip_serializing_if = "Option::is_none")] pub enable_file_hooks: Option, /// Whether to enable host git operations (context resolution, child repo scanning, git info in system prompt). @@ -25240,7 +27446,7 @@ pub struct SessionUpdateOptionsParams { /// Whether to enable cross-session store writes and reads. #[serde(skip_serializing_if = "Option::is_none")] pub enable_session_store: Option, - /// Whether skill loading is enabled. Explicit false disables every source, including a bound SDK provider; changing the value invalidates the loaded skill snapshot. When omitted, creation falls back to enableConfigDiscovery unless an SDK skill provider is registered. + /// Whether skill loading is enabled. Explicit false disables every source, including explicit SDK roots and a bound SDK provider; changing the value invalidates the loaded skill snapshot. When omitted, creation falls back to enableConfigDiscovery unless explicit SDK skill roots or an SDK skill provider are supplied. #[serde(skip_serializing_if = "Option::is_none")] pub enable_skills: Option, /// Whether to stream model responses. @@ -25264,6 +27470,9 @@ pub struct SessionUpdateOptionsParams { /// Map of feature-flag IDs to their boolean enabled state. #[serde(skip_serializing_if = "Option::is_none")] pub feature_flags: Option>, + /// Explicit discovery-only file-hook roots, separate from callback hooks and filesystem grants. Available without enableConfigDiscovery or enableFileHooks, which controls repository-origin hooks only; hook/managed disables still apply. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. + #[serde(skip_serializing_if = "Option::is_none")] + pub hook_directories: Option>, /// Skill scan directories and descendants excluded from discovery. Supports `~`-relative paths. #[serde(skip_serializing_if = "Option::is_none")] pub ignored_skills_locations: Option>, @@ -25276,6 +27485,9 @@ pub struct SessionUpdateOptionsParams { /// Full set of installed plugins for the session. Replaces the existing list; the runtime invalidates the skills cache only when the list materially changes. #[serde(skip_serializing_if = "Option::is_none")] pub installed_plugins: Option>, + /// Explicit discovery-only instruction roots using existing SDK instruction formats, not filesystem grants. Available without enableConfigDiscovery, subject to instruction/managed disables. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. + #[serde(skip_serializing_if = "Option::is_none")] + pub instruction_directories: Option>, /// Stable integration identifier used for analytics and rate-limit attribution. #[serde(skip_serializing_if = "Option::is_none")] pub integration_id: Option, @@ -25345,7 +27557,7 @@ pub struct SessionUpdateOptionsParams { /// PowerShell process flags applied to built-in and user-requested shell commands. #[serde(skip_serializing_if = "Option::is_none")] pub shell_process_flags: Option>, - /// Additional directories to search for skills. + /// Explicit discovery-only skill roots, not filesystem grants. Available without enableConfigDiscovery, subject to skill/managed disables. Relative SDK paths resolve once when supplied against the request's effective workingDirectory; retained roots do not move on cwd changes. Null and omission are equivalent; [] clears only the explicit SDK contribution. #[serde(skip_serializing_if = "Option::is_none")] pub skill_directories: Option>, /// Whether to skip loading custom instruction sources. @@ -25389,6 +27601,22 @@ pub struct SessionUpdateOptionsResult { pub success: bool, } +/// Outcome of an atomic user or repository settings edit. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct SettingsEditResult { + /// Changed, unchanged or invalid existing file. Rejected input, validation failures and I/O errors remain RPC errors. + #[doc(hidden)] + pub(crate) status: SettingsEditStatus, +} + /// User-requested shell execution cancellation handle. /// ///
@@ -26153,7 +28381,7 @@ pub struct SkillsDiscoverRequest { /// Optional list of project directory paths to scan for project-scoped skills #[serde(skip_serializing_if = "Option::is_none")] pub project_paths: Option>, - /// Optional list of additional skill directory paths to include + /// Explicit discovery-only skill roots, not filesystem grants. Available without ambient discovery, subject to skill/managed disables. Relative paths resolve against the request's effective working directory when supplied. Null and omission are equivalent; an empty array contributes no explicit roots. #[serde(skip_serializing_if = "Option::is_none")] pub skill_directories: Option>, } @@ -26216,6 +28444,70 @@ pub struct SkillsGetDiscoveryPathsRequest { pub project_paths: Option>, } +/// Extensible [`SkillsGetDiscoveryPathsRequest`], including inputs added after it was published. +/// +/// Required inputs are [`SkillsGetDiscoveryPathsOptions::new`] arguments; optional inputs have fluent setters. +/// Input-only: it serialises to the flat wire request and is not deserialisable. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct SkillsGetDiscoveryPathsOptions { + #[serde(flatten)] + legacy: SkillsGetDiscoveryPathsRequest, + #[serde(skip_serializing_if = "Option::is_none")] + skill_directories: Option>, +} + +impl SkillsGetDiscoveryPathsOptions { + /// Creates options with the required inputs. + pub fn new() -> Self { + Self { + legacy: SkillsGetDiscoveryPathsRequest { + project_paths: None, + ignored_skills_locations: None, + exclude_host_skills: None, + }, + skill_directories: None, + } + } + + /// Optional list of project directory paths. When omitted or empty, only personal and custom directories are returned. + pub fn project_paths(mut self, value: Vec) -> Self { + self.legacy.project_paths = Some(value); + self + } + + /// Optional skill scan paths to exclude from discovery. + pub fn ignored_skills_locations(mut self, value: Vec) -> Self { + self.legacy.ignored_skills_locations = Some(value); + self + } + + /// When true, omit the host's personal and custom skill directories, leaving only project directories. For multitenant deployments. + pub fn exclude_host_skills(mut self, value: bool) -> Self { + self.legacy.exclude_host_skills = Some(value); + self + } + + /// Explicit discovery-only skill roots to enumerate, including configured missing directories. Relative paths resolve against the request's effective working directory when supplied. These are not filesystem grants or preferred creation targets. Null and omission are equivalent. + pub fn skill_directories(mut self, value: Vec) -> Self { + self.skill_directories = Some(value); + self + } +} + +impl Default for SkillsGetDiscoveryPathsOptions { + fn default() -> Self { + Self::new() + } +} + /// Skill invocation record with name, path, content, allowed tools, and turn number. /// ///
@@ -28494,6 +30786,153 @@ pub struct UserSettingMetadata { pub value: serde_json::Value, } +/// A single top-level setting subtree to validate and write. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsApplyTopKeyRequest { + /// Canonical path of the setting the caller is editing. Currently informational only. + pub canonical_path: Vec, + /// Records that the user chose an update channel explicitly, so the one-time staff channel migration stops reapplying. Honored only for the `autoUpdatesChannel` key. + #[serde(skip_serializing_if = "Option::is_none")] + pub complete_staff_update_channel_migration: Option, + /// True to remove the top-level key instead of writing it. + #[serde(skip_serializing_if = "Option::is_none")] + pub delete: Option, + /// Top-level setting name to write. + pub top_key: String, + /// Value to write for the whole top-level key. Omitted when `delete` is true. Explicit null is preserved and validated as null. + #[serde(skip_serializing_if = "Option::is_none")] + pub value: Option, +} + +/// One problem found while validating a setting value. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsValidationIssue { + /// Human-readable description of the problem. + pub message: String, + /// Where in the settings tree the problem sits, as object keys and array indices. The first element is the top-level key; a whole-value problem is reported as that single key. + pub path: Vec, +} + +/// Outcome of validating and writing one top-level setting subtree. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsApplyTopKeyResult { + /// Why the write failed, present only when `status` is `write-error`. + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, + /// Problems found, present only when `status` is `validation-error`. + #[serde(skip_serializing_if = "Option::is_none")] + pub issues: Option>, + /// `ok` when the value was written, `validation-error` when it was rejected before any write, `write-error` when validation passed but the write failed. + pub status: String, +} + +/// The overrides and host context to resolve effective runtime settings from. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserSettingsBuildRuntimeRequest { + /// The environment to read settings-bearing variables from. Passed explicitly rather than read from the server process so a host can resolve settings for an environment it is about to create rather than the one it runs in. + pub environment: HashMap, + /// Settings that take precedence over every document and environment layer, such as the values a host derived from its own command line. Keyed by setting name, with each value carried verbatim, so a host may pass keys this runtime does not recognize. Absent when the host contributes no overrides of its own. + #[serde(skip_serializing_if = "Option::is_none")] + pub overrides: Option>, + /// The platform to apply platform-specific defaults for, in the same spelling the host's own runtime reports. + pub platform: String, + /// The directory the settings are being resolved for. This selects the repository-scoped document, so a host resolving settings for a directory other than its own process working directory must pass that directory here. + pub working_directory: String, +} + +/// One atomic edit of a canonical setting in settings.json. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserSettingsEditRequest { + /// Nonempty canonical path segments of the setting to edit, starting with its top-level key. + pub canonical_path: Vec, + /// Configuration directory to edit. Omitted or empty resolves the server's own home, as in `user.settings.loadForConfigDir`. + #[serde(skip_serializing_if = "Option::is_none")] + pub config_dir: Option, + /// Set a scalar, add or remove a string list item, or remove the setting. The handler rejects a missing or incompatible value. + #[doc(hidden)] + pub(crate) operation: SettingsEditOperation, + /// Required boolean, string or number for set, required string for add-item and remove-item, and forbidden for remove. The runtime rejects all other JSON values. + #[serde(skip_serializing_if = "Option::is_none")] + pub value: Option, +} + +/// Selects which settings file paths to resolve. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsGetPathsRequest { + /// Absolute path of a repository root. Supply it to also resolve that repository's two settings file paths; omit it to resolve only the user path. + #[serde(skip_serializing_if = "Option::is_none")] + pub repo_root: Option, +} + +/// On-disk locations of the settings files this runtime reads. A path is reported whether or not the file exists. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsGetPathsResult { + /// The repository's local, untracked settings file, or null when no repository root was supplied. + #[serde(skip_serializing_if = "Option::is_none")] + pub repo_local_settings: Option, + /// The repository's checked-in settings file, or null when no repository root was supplied. + #[serde(skip_serializing_if = "Option::is_none")] + pub repo_settings: Option, + /// The user settings file: `settings.json` under the resolved Copilot home. This is the file `user.settings.get`, `user.settings.load`, and `user.settings.set` read and write. + pub user_settings: String, +} + /// Per-key metadata for every known user setting in settings.json, including settings left at their default. Excludes repository- and enterprise-managed overrides. /// ///
@@ -28509,6 +30948,549 @@ pub struct UserSettingsGetResult { pub settings: HashMap, } +/// Top-level user-setting keys grouped by who may govern them. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsGovernanceKeysResult { + /// Top-level keys a repository may override through a Claude-compatible repository config. + pub claude_repo: Vec, + /// Top-level keys an enterprise policy may fix through managed settings. + pub managed: Vec, + /// Top-level keys a repository may override through `.github/copilot/settings.json` or `.github/copilot/settings.local.json`. + pub repo: Vec, +} + +/// The filesystem and environment facts a user-settings file is resolved from. Passed explicitly rather than read from the runtime's own process, so a host whose configuration directory differs from its process environment resolves the file it means. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsHostContext { + /// The Copilot configuration directory, when the host set one. Takes precedence over `COPILOT_HOME` and the home directory. + #[serde(skip_serializing_if = "Option::is_none")] + pub config_dir: Option, + /// The host's environment variables. `COPILOT_HOME` is read from here, as are the variables a settings hook may expand. + pub environment: serde_json::Value, + /// The user's home directory, used when neither a configuration directory nor `COPILOT_HOME` is set. + pub home_directory: String, +} + +/// A repository to read settings from, and the user layer they are merged over. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserSettingsLoadAndMergeRepoRequest { + /// Absolute path of the repository root whose settings to read. When this is the user's home directory, the Claude-compatible configuration is skipped, so a home-directory session does not inherit a personal config as if it were repository policy. + pub repo_root: String, + /// The user's own settings, keyed by setting name. The repository layer is merged over this; a repository may only add to the collection settings it is allowed to influence, so a user setting it does not name survives unchanged. + pub user_settings: HashMap, +} + +/// The settings a consumer should act on inside a repository, together with any diagnostic raised while reading the repository layer. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserSettingsLoadAndMergeRepoResult { + /// The merged settings document, keyed by setting name. Equals the supplied user layer when the repository declares no settings. + pub settings: HashMap, + /// Why part of the repository document was ignored, as a single human-readable string joining every diagnostic with `; `. Null when it loaded cleanly. The settings that did load are still merged, so a consumer should surface this and continue rather than treat it as a failure. + #[serde(skip_serializing_if = "Option::is_none")] + pub warning: Option, +} + +/// Selects the configuration directory whose user settings to read. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserSettingsLoadForConfigDirRequest { + /// Copilot configuration directory to read the settings document from, taking precedence over the server's own `COPILOT_HOME` and default home. Omit it, or pass an empty string, to read the directory the server resolved for itself. + #[serde(skip_serializing_if = "Option::is_none")] + pub config_dir: Option, +} + +/// Contents of settings.json as stored, for a caller about to rewrite it. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsLoadForEditResult { + /// Parsed contents, present only when `status` is `ok`. + #[serde(skip_serializing_if = "Option::is_none")] + pub settings: Option, + /// `ok` when the file parsed, `missing` when there is no file yet, `invalid` when the file exists but will not parse. + pub status: String, +} + +/// Selects the repository whose checked-in settings to read. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsLoadRepoRequest { + /// Absolute path of the repository root whose settings to read. When this is the user's home directory, the Claude-compatible configuration is skipped, so a home-directory session does not inherit a personal config as if it were repository policy. + pub repo_root: String, +} + +/// A repository's settings as stored, together with any diagnostic raised while reading them. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsLoadRepoResult { + /// The repository's settings document, keyed by setting name, with each value carried verbatim. Null when the repository declares no settings. + #[serde(skip_serializing_if = "Option::is_none")] + pub settings: Option>, + /// Why part of the document was ignored, as a single human-readable string joining every diagnostic with `; `. Null when the document loaded cleanly. The settings that did load are still reported, so a consumer should surface this and continue rather than treat it as a failure. + #[serde(skip_serializing_if = "Option::is_none")] + pub warning: Option, +} + +/// The user's stored settings, together with any diagnostic raised while reading them. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsLoadWithWarningResult { + /// The stored user settings document, keyed by setting name, with each value carried verbatim. Empty when the file could not be read at all. + pub settings: HashMap, + /// Why part of the document was ignored, phrased for display to the user. Null when the document loaded cleanly. The settings that did load are still reported, so a consumer may surface this and continue rather than treat it as a failure. + #[serde(skip_serializing_if = "Option::is_none")] + pub warning: Option, +} + +/// The managed (enterprise policy) layer and the user layer to fold together. The field names carry the layer identity: nothing about a settings object identifies which layer produced it, so a generic base/override pair would let a caller transpose them and silently resolve the governance keys in the user's favour. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserSettingsMergeManagedAndUserParams { + /// The managed layer. Its governance keys win, and the per-entry `enabledPlugins` and `extraKnownMarketplaces` control maps are stamped back from it after the union. Omitted when the host is unmanaged. + #[serde(skip_serializing_if = "Option::is_none")] + pub managed: Option, + /// The user's own settings layer. Wins on every key the managed layer does not govern. Omitted when the user has no settings yet. + #[serde(skip_serializing_if = "Option::is_none")] + pub user: Option, +} + +/// The user layer folded over the managed layer. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsMergeManagedAndUserResult { + /// The merged settings. An empty object when neither layer was supplied, so an unmanaged host with no user settings governs nothing. + pub settings_json: serde_json::Value, +} + +/// The user layer and the repository layer to fold together. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserSettingsMergeRepoValueParams { + /// The repository's checked-in settings layer. + pub repo_settings: serde_json::Value, + /// The user's settings, already resolved against any managed layer. + pub user_settings: serde_json::Value, +} + +/// The repository layer folded into the user layer. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsMergeRepoValueResult { + /// The merged settings, with the per-entry managed control maps re-stamped from the runtime's ambient managed policy. + pub settings_json: serde_json::Value, +} + +/// Selects the configuration directory whose settings document to clean. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserSettingsMigrateObsoleteOnDiskRequest { + /// The configuration directory holding the settings document to clean. A host that scopes a session to its own Copilot home passes that directory here; omit it to clean the document under the home the server resolved for itself. The home directory and environment are deliberately not caller-supplied: they decide only the settings path root and the two hook-parsing flags, and those describe the process doing the reading rather than the caller. + #[serde(skip_serializing_if = "Option::is_none")] + pub config_dir: Option, +} + +/// The outcome of the one-time obsolete-settings cleanup. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserSettingsMigrateObsoleteOnDiskResult { + /// What the cleanup did. Only `changed` rewrote the document. The cleaned settings are not returned here: the document is written before this replies, so `user.settings.load` reads it authoritatively. + #[doc(hidden)] + pub(crate) status: UserSettingsMigrateObsoleteOnDiskStatus, +} + +/// Selects the configuration directory to run the one-time staff log-level migration against, and supplies the caller's record of whether it has already run. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserSettingsMigrateStaffLogLevelOnceRequest { + /// When the caller last recorded this migration, as an ISO-8601 timestamp. Supplying it lets the runtime answer `current` without re-running the evaluation. + #[serde(skip_serializing_if = "Option::is_none")] + pub cached_completed_at: Option, + /// The configuration directory, home directory and environment to resolve the settings and global-state documents from. A host that scopes a session to its own Copilot home must pass that directory here, or the migration is evaluated against the server's own home instead. + pub context: UserSettingsHostContext, +} + +/// The outcome of the one-time staff log-level migration. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserSettingsMigrateStaffLogLevelOnceResult { + /// When the migration was recorded as done, as an ISO-8601 timestamp. Absent only for `ineligible`, which records nothing. + #[serde(skip_serializing_if = "Option::is_none")] + pub completed_at: Option, + /// The settings document as it stands after the migration, keyed by setting name. Present only for `refreshed`, and only when the document could be re-read cleanly. + #[serde(skip_serializing_if = "Option::is_none")] + pub settings: Option, + /// What the migration did. Only `refreshed` can have changed the persisted settings. + pub status: StaffLogLevelMigrationStatus, +} + +/// The outcome of the one-time staff update-channel migration. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserSettingsMigrateStaffUpdateChannelOnceResult { + /// When the migration was recorded as done, as an ISO-8601 timestamp. Absent only for `ineligible`, which records nothing. + #[serde(skip_serializing_if = "Option::is_none")] + pub completed_at: Option, + /// What the migration did. Only `migrated` means the persisted settings changed. + pub status: StaffUpdateChannelMigrationStatus, +} + +/// Settings object whose key names should be canonicalized. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsNormalizeRequest { + /// Free-form settings object, possibly using legacy or snake_case key names. + pub settings: serde_json::Value, +} + +/// Settings object with canonical key names. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsNormalizeResult { + /// The same settings with every legacy or snake_case key renamed to its canonical spelling. + pub settings: serde_json::Value, +} + +/// The on-disk location of the user's settings document. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsPathResult { + /// Absolute path to the settings file this runtime reads and writes. Normally `settings.json` under the Copilot configuration directory; an extensionless legacy `settings` file is reported instead when it exists and `settings.json` does not. + pub path: String, +} + +/// The settings layer a problem was found in. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsProblemLocation { + /// Absolute path of the file backing this layer. + pub file_path: String, + /// Which layer this is: `user`, `repo`, or `local`. + pub kind: String, + /// The caller-supplied display label for the layer. + pub label: String, +} + +/// One setting that will not take effect, and why. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsProblem { + /// Coarse grouping for the problem, for a caller that presents problems in sections. + pub category: String, + /// Why the setting will not take effect. Never contains the setting's value, which may be a token or another secret. + pub explanation: String, + /// Stable identifier for this problem within the reported set, so a caller can track a selection across refreshes. + pub id: String, + /// What is wrong in machine-readable form, for a caller that wants to branch rather than display. + pub kind: String, + /// The offending key as written in the file, when the problem is attributable to a single key. Absent when the whole file is at fault. + #[serde(skip_serializing_if = "Option::is_none")] + pub raw_key: Option, + /// The settings layer the problem was found in. + pub source: UserSettingsProblemLocation, +} + +/// One already-loaded settings layer to diagnose. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsProblemSource { + /// The layer's settings exactly as loaded, unnormalized. Empty when the file is missing or failed to parse. + #[serde(skip_serializing_if = "Option::is_none")] + pub config: Option>, + /// Absolute path of the file backing this layer. + pub file_path: String, + /// True when the file itself failed to parse, so its keys could not be read at all. + #[serde(skip_serializing_if = "Option::is_none")] + pub invalid: Option, + /// Which layer this is: `user`, `repo`, or `local`. + pub kind: String, + /// Caller-chosen display label for the layer, echoed back on each problem so a caller can group by layer without re-deriving its own naming. + pub label: String, +} + +/// The settings layers to diagnose. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsProblemsRequest { + /// Layers to diagnose, in the precedence order the caller applies them. + #[serde(skip_serializing_if = "Option::is_none")] + pub sources: Option>, +} + +/// The settings that will not take effect across the given layers. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsProblemsResult { + /// Each setting that will not take effect, and why. Empty when every layer is healthy. + #[serde(skip_serializing_if = "Option::is_none")] + pub problems: Option>, +} + +/// Selects the configuration directory to run the one-time staff model-selection reset against, and supplies the caller's record of whether it has already run. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserSettingsResetStaffModelSelectionOnceRequest { + /// The configuration directory, home directory and environment to resolve the settings and global-state documents from. A host that scopes a session to its own Copilot home must pass that directory here, or the reset is evaluated against the server's own home instead. + pub context: UserSettingsHostContext, + /// Whether the signed-in account is a staff account. The reset applies only to staff, so anything other than `true` returns `didReset: false` without touching the settings document. + #[serde(skip_serializing_if = "Option::is_none")] + pub staff: Option, + /// When the caller last recorded this reset, as an ISO-8601 timestamp. A timestamp at or after the cutoff means the reset already ran and is returned unchanged. + #[serde(skip_serializing_if = "Option::is_none")] + pub staff_model_reset_at: Option, +} + +/// The outcome of the one-time staff model-selection reset. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserSettingsResetStaffModelSelectionOnceResult { + /// The cutoff this reset is gated on, as an ISO-8601 timestamp. A recorded timestamp at or after it means the reset is already done. + pub cutoff: String, + /// Whether the persisted model selection was cleared. Only `true` means the settings document changed and the host should adopt the returned settings. + pub did_reset: bool, + /// The timestamp the host should now record, as an ISO-8601 timestamp. Carries the caller's own value back unchanged when nothing was reset. + #[serde(skip_serializing_if = "Option::is_none")] + pub reset_at: Option, + /// The settings document as it stands after the reset, keyed by setting name. Present only when `didReset` is true. + #[serde(skip_serializing_if = "Option::is_none")] + pub settings: Option, +} + +/// One hook to disable or re-enable. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserSettingsSetHookDisabledRequest { + /// True adds the key to `disabledHooks`, false removes it. + pub disabled: bool, + /// Durable content-hash key of the hook, as `hooks.loadResources` reports it. + pub disable_key: String, + /// Path of the settings.json to rewrite. + pub settings_path: String, +} + +/// Outcome of rewriting the `disabledHooks` setting. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserSettingsSetHookDisabledResult { + /// Why the write failed. Absent when the status is `ok`. + #[serde(skip_serializing_if = "Option::is_none")] + pub error_message: Option, + /// `ok` when the setting was written, `invalid` when settings.json could not be parsed and was left alone, `error` when the write itself failed. + pub status: String, +} + /// Partial user settings to write to settings.json. Each top-level key is written individually, replacing the existing value; a key whose value is null is removed. /// ///
@@ -28524,6 +31506,129 @@ pub struct UserSettingsSetRequest { pub settings: serde_json::Value, } +/// Selects the configuration directory whose user settings document should be inspected for unrecognized top-level keys. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserSettingsUnknownKeysWarningRequest { + /// The configuration directory, home directory and environment to resolve the settings document from. A host that scopes a session to its own Copilot home must pass that directory here, or the server's own home is inspected instead. + pub context: UserSettingsHostContext, +} + +/// The unrecognized top-level keys found in the user settings document, already phrased for display. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserSettingsUnknownKeysWarningResult { + /// The warning to show the user, naming the unrecognized keys and the file they were found in. Absent when every top-level key is recognized, and also when the document is missing or could not be read, because an unknown-key list taken from a document the runtime could not load would name keys that may not be there. + #[serde(skip_serializing_if = "Option::is_none")] + pub warning: Option, +} + +/// Names the user setting to merge into, and the partial object to merge. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserSettingsUpdateRequest { + /// Copilot configuration directory to write the settings document in, taking precedence over the server's own `COPILOT_HOME` and default home. Omit it, or pass an empty string, to use the directory the server resolved for itself. + #[serde(skip_serializing_if = "Option::is_none")] + pub config_dir: Option, + /// Top-level user-setting key to merge into, such as `extensions`. + pub key: String, + /// Single-component name for the settings document at `/settings/.json`; path separators, `.` and `..` are rejected. Omit it, or pass an empty string, to use the main settings document. + #[serde(skip_serializing_if = "Option::is_none")] + pub sub_dir: Option, + /// Value to store for `key`. When both the stored value and this value are objects, their fields are shallow-merged and fields this object does not name are preserved. Otherwise this value replaces the stored value. + pub value: serde_json::Value, +} + +/// The stored value of the user setting after the merge. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserSettingsUpdateResult { + /// The key's stored value after the write: the merged object when both the stored value and the supplied value are objects, otherwise the supplied value. + pub value: serde_json::Value, +} + +/// A single top-level setting subtree to validate. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsValidateRequest { + /// True to validate a removal of this key. The current settings file is not read. + #[serde(skip_serializing_if = "Option::is_none")] + pub delete: Option, + /// Top-level setting name to validate. + pub top_key: String, + /// Value to validate. Omitted when `delete` is true. + #[serde(skip_serializing_if = "Option::is_none")] + pub value: Option, +} + +/// Outcome of validating one top-level setting subtree. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsValidateResult { + /// Problems found, empty when `ok` is true. + #[serde(skip_serializing_if = "Option::is_none")] + pub issues: Option>, + /// True when the subtree is valid. + pub ok: bool, +} + +/// The whole content to store in the user settings file. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsWriteAllRequest { + /// The settings object to store. It replaces the file's entire content, so a key absent here is removed. + pub settings: serde_json::Value, +} + /// Current sharing status and shareable GitHub URL for a session. /// ///
@@ -30198,6 +33303,360 @@ pub struct WorkspacesWriteAutopilotObjectiveResult { pub operation: String, } +/// Status of the persistent certificate authority of the sandbox credential proxy. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SandboxProxyCaGetStatusResult { + /// Whether this process can add the certificate authority to OS trust without credentials from a different user. False where OS trust is unsupported, and on Windows when the process cannot elevate itself to write the machine trust store. When false, do not offer to set up the certificate authority. + pub can_install: bool, + /// Human-readable reason for the state. On `installed` or `notInstalled`, present only when the certificate authority must be rotated, and then says why. + #[serde(skip_serializing_if = "Option::is_none")] + pub detail: Option, + /// The state of the certificate authority. + pub state: SandboxProxyCaState, +} + +/// Status of the persistent certificate authority of the sandbox credential proxy. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SandboxProxyCaRotateResult { + /// Whether this process can add the certificate authority to OS trust without credentials from a different user. False where OS trust is unsupported, and on Windows when the process cannot elevate itself to write the machine trust store. When false, do not offer to set up the certificate authority. + pub can_install: bool, + /// Human-readable reason for the state. On `installed` or `notInstalled`, present only when the certificate authority must be rotated, and then says why. + #[serde(skip_serializing_if = "Option::is_none")] + pub detail: Option, + /// The state of the certificate authority. + pub state: SandboxProxyCaState, +} + +/// Status of the persistent certificate authority of the sandbox credential proxy. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SandboxProxyCaTrustResult { + /// Whether this process can add the certificate authority to OS trust without credentials from a different user. False where OS trust is unsupported, and on Windows when the process cannot elevate itself to write the machine trust store. When false, do not offer to set up the certificate authority. + pub can_install: bool, + /// Human-readable reason for the state. On `installed` or `notInstalled`, present only when the certificate authority must be rotated, and then says why. + #[serde(skip_serializing_if = "Option::is_none")] + pub detail: Option, + /// The state of the certificate authority. + pub state: SandboxProxyCaState, +} + +/// Status of the persistent certificate authority of the sandbox credential proxy. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SandboxProxyCaRemoveResult { + /// Whether this process can add the certificate authority to OS trust without credentials from a different user. False where OS trust is unsupported, and on Windows when the process cannot elevate itself to write the machine trust store. When false, do not offer to set up the certificate authority. + pub can_install: bool, + /// Human-readable reason for the state. On `installed` or `notInstalled`, present only when the certificate authority must be rotated, and then says why. + #[serde(skip_serializing_if = "Option::is_none")] + pub detail: Option, + /// The state of the certificate authority. + pub state: SandboxProxyCaState, +} + +/// Where this runtime keeps the sandbox proxy password. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SandboxProxySecretGetLocationResult { + /// Absolute path of the credential store holding the sandbox proxy password. The proxy password belongs to no MCP server, so it is kept in its own directory rather than in the MCP secret store. + pub config_dir: String, + /// Identifier of the proxy password entry within that store. Derived from the configuration directory, so two Copilot homes on one machine never share an entry. Use it to recognize a `${secret:…}` reference in stored settings as this runtime's own proxy password. + pub secret_id: String, +} + +/// The sandbox a named feature actually launches under. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct SandboxResolveForOptionalResult { + /// The effective sandbox config for the requested feature, or a disabled sandbox when the feature is routed out of it. + pub sandbox_json: serde_json::Value, +} + +/// The startup warning, absent when the machine can run a sandbox. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct SandboxGetUnsupportedStartupNoticeResult { + /// The user-facing warning, carrying the platform remedy and the recovery the policy in force actually allows. Absent when this machine supports a sandbox, so a host can call unconditionally and print only what it gets. + #[serde(skip_serializing_if = "Option::is_none")] + pub notice: Option, +} + +/// The documentation URL a host cites when it declines or downgrades a sandbox setting. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SandboxGetDocsUrlResult { + /// Documentation URL describing the sandbox and the settings that control it. + pub url: String, +} + +/// Whether the host running this runtime can run the command sandbox. The runtime checks `supported` once per process. A capability answer can change while the process runs, for example after the user installs a missing package. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SandboxGetHostSupportResult { + /// Sandbox policy features whose availability varies between hosts that can run the backend. Empty when `supported` is false, because no feature can run without a backend. Later runtimes can add entries; ignore an entry whose `name` you do not recognize. + pub capabilities: Vec, + /// Human-readable reason the sandbox cannot run on this host. Present only when `supported` is false. + #[serde(skip_serializing_if = "Option::is_none")] + pub reason: Option, + /// Whether a process-containment backend is usable on this host: Seatbelt on macOS, Bubblewrap on Linux, or ProcessContainer on Windows. + pub supported: bool, +} + +/// The on-disk location of the user's settings document. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsGetPathResult { + /// Absolute path to the settings file this runtime reads and writes. Normally `settings.json` under the Copilot configuration directory; an extensionless legacy `settings` file is reported instead when it exists and `settings.json` does not. + pub path: String, +} + +/// The unrecognized top-level keys found in the user settings document, already phrased for display. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserSettingsGetUnknownKeysWarningResult { + /// The warning to show the user, naming the unrecognized keys and the file they were found in. Absent when every top-level key is recognized, and also when the document is missing or could not be read, because an unknown-key list taken from a document the runtime could not load would name keys that may not be there. + #[serde(skip_serializing_if = "Option::is_none")] + pub warning: Option, +} + +/// Top-level user-setting keys grouped by who may govern them. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsListGovernanceKeysResult { + /// Top-level keys a repository may override through a Claude-compatible repository config. + pub claude_repo: Vec, + /// Top-level keys an enterprise policy may fix through managed settings. + pub managed: Vec, + /// Top-level keys a repository may override through `.github/copilot/settings.json` or `.github/copilot/settings.local.json`. + pub repo: Vec, +} + +/// Outcome of an atomic user or repository settings edit. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserSettingsEditResult { + /// Changed, unchanged or invalid existing file. Rejected input, validation failures and I/O errors remain RPC errors. + #[doc(hidden)] + pub(crate) status: SettingsEditStatus, +} + +/// The settings that will not take effect across the given layers. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserSettingsDiagnoseProblemsResult { + /// Each setting that will not take effect, and why. Empty when every layer is healthy. + #[serde(skip_serializing_if = "Option::is_none")] + pub problems: Option>, +} + +/// Outcome of an atomic user or repository settings edit. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct RepoSettingsEditResult { + /// Changed, unchanged or invalid existing file. Rejected input, validation failures and I/O errors remain RPC errors. + #[doc(hidden)] + pub(crate) status: SettingsEditStatus, +} + +/// Every feature flag this runtime declares, with the shape each one was declared with. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct FeatureFlagsListMetadataResult { + /// Declared feature flags keyed by flag name. + #[doc(hidden)] + pub(crate) flags: HashMap, +} + +/// Which stored location permissions were dropped because their directory is gone. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct PermissionsLocationsCleanupOrphanedResult { + /// Location keys whose grants were removed. Empty when every stored location still exists. + pub removed_location_keys: Vec, +} + +/// Folder trust check result. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PermissionsFolderTrustCheckTrustResult { + /// Whether the folder is trusted + pub trusted: bool, +} + +/// The verdict of the `forceLoginOrgs` enterprise sign-in pin for one credential. A managed device may pin Copilot to an approved set of GitHub organizations so a developer cannot sign in with a personal — or another enterprise's — account and escape every control that keys off the signed-in identity. A host must refuse to proceed when `allowed` is false; the remaining fields explain the refusal rather than qualifying it. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ManagedSettingsResolveDeviceForceLoginOrgsGateResult { + /// Whether the client may proceed. True whenever no pin is in force, so an unmanaged host is never blocked. + pub allowed: bool, + /// A stable kebab-case reason code when refused: `org-not-approved`, `unverifiable-credential`, `policy-undetermined`, `policy-invalid`, `not-signed-in`, or `membership-unknown`. Omitted when allowed. Treat an unrecognized code as a refusal rather than mapping it to an allow: a code this host has not been taught about still denied the credential. + #[serde(skip_serializing_if = "Option::is_none")] + pub denial: Option, + /// Whether a pin applies at all. Distinguishes "allowed because unmanaged" from "allowed because the account is approved", which a diagnostics surface needs and which also decides whether a bring-your-own-key provider may serve inference. + pub enforced: bool, + /// The approved organization the account matched, when allowed under a pin. + #[serde(skip_serializing_if = "Option::is_none")] + pub matched_org: Option, + /// A developer-facing explanation of the refusal. Omitted when allowed. + #[serde(skip_serializing_if = "Option::is_none")] + pub message: Option, + /// Error-level detail from managed-policy discovery — which file failed to load, which field failed validation. Carried on the decision rather than left to the log because a refusal can exit the process before a log sink exists, which would discard the only actionable diagnostic a misconfigured managed device produces. + pub policy_diagnostics: Vec, + /// The approved organization logins, as configured. Enterprise configuration rather than user data, so it is safe to name in a message or a log line. + pub required_orgs: Vec, +} + +/// Ordered managed permission verdicts and the supplied policy's fail-closed posture. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ManagedSettingsPermissionsEvaluateResult { + /// Managed permission policy could not be determined; every operation is denied. + pub fail_closed: bool, + /// One verdict per input operation, in the same order, including duplicates. + pub results: Vec, +} + /// The resident session the application has materialized on its own connection. /// ///
@@ -30323,7 +33782,7 @@ pub struct ModelsGetBuiltInCatalogResult { pub models: Vec, } -/// Whether the host running this runtime can run the command sandbox. The runtime checks `supported` once per process. A capability answer can change while the process runs, for example after the user installs a missing package. +/// Built-in tools available for the requested model, with their parameters and instructions. /// ///
/// @@ -30333,17 +33792,12 @@ pub struct ModelsGetBuiltInCatalogResult { ///
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct SandboxGetHostSupportResult { - /// Sandbox policy features whose availability varies between hosts that can run the backend. Empty when `supported` is false, because no feature can run without a backend. Later runtimes can add entries; ignore an entry whose `name` you do not recognize. - pub capabilities: Vec, - /// Human-readable reason the sandbox cannot run on this host. Present only when `supported` is false. - #[serde(skip_serializing_if = "Option::is_none")] - pub reason: Option, - /// Whether a process-containment backend is usable on this host: Seatbelt on macOS, Bubblewrap on Linux, or ProcessContainer on Windows. - pub supported: bool, +pub struct ToolsListResult { + /// List of available built-in tools with metadata + pub tools: Vec, } -/// Status of the persistent certificate authority of the sandbox credential proxy. +/// User-configured MCP servers, keyed by server name. /// ///
/// @@ -30353,17 +33807,12 @@ pub struct SandboxGetHostSupportResult { ///
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct SandboxProxyCaGetStatusResult { - /// Whether this process can add the certificate authority to OS trust without credentials from a different user. False where OS trust is unsupported, and on Windows when the process cannot elevate itself to write the machine trust store. When false, do not offer to set up the certificate authority. - pub can_install: bool, - /// Human-readable reason for the state. On `installed` or `notInstalled`, present only when the certificate authority must be rotated, and then says why. - #[serde(skip_serializing_if = "Option::is_none")] - pub detail: Option, - /// The state of the certificate authority. - pub state: SandboxProxyCaState, +pub struct McpConfigListResult { + /// All MCP servers from user config, keyed by name + pub servers: HashMap, } -/// Status of the persistent certificate authority of the sandbox credential proxy. +/// Request id naming a cancellable registry search. /// ///
/// @@ -30373,17 +33822,12 @@ pub struct SandboxProxyCaGetStatusResult { ///
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct SandboxProxyCaRotateResult { - /// Whether this process can add the certificate authority to OS trust without credentials from a different user. False where OS trust is unsupported, and on Windows when the process cannot elevate itself to write the machine trust store. When false, do not offer to set up the certificate authority. - pub can_install: bool, - /// Human-readable reason for the state. On `installed` or `notInstalled`, present only when the certificate authority must be rotated, and then says why. - #[serde(skip_serializing_if = "Option::is_none")] - pub detail: Option, - /// The state of the certificate authority. - pub state: SandboxProxyCaState, +pub(crate) struct McpRegistryAllocateRequestIdResult { + /// Request ID for `mcp.registry.search` and `mcp.registry.cancel`. It serves one search. Allocation can reclaim unused IDs at the 1,024-ID reservation limit. + pub request_id: i64, } -/// Status of the persistent certificate authority of the sandbox credential proxy. +/// Extensions discovered from persisted Copilot home state and their effective loading mode. Launch-scoped additional plugins are not included. /// ///
/// @@ -30393,17 +33837,14 @@ pub struct SandboxProxyCaRotateResult { ///
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct SandboxProxyCaTrustResult { - /// Whether this process can add the certificate authority to OS trust without credentials from a different user. False where OS trust is unsupported, and on Windows when the process cannot elevate itself to write the machine trust store. When false, do not offer to set up the certificate authority. - pub can_install: bool, - /// Human-readable reason for the state. On `installed` or `notInstalled`, present only when the certificate authority must be rotated, and then says why. - #[serde(skip_serializing_if = "Option::is_none")] - pub detail: Option, - /// The state of the certificate authority. - pub state: SandboxProxyCaState, +pub struct ExtensionsDiscoverResult { + /// Discovered user and enabled installed-plugin extensions from persisted Copilot home state + pub extensions: Vec, + /// Effective extension loading mode. Defaults to load_and_augment when unset. + pub mode: DiscoveredExtensionMode, } -/// Status of the persistent certificate authority of the sandbox credential proxy. +/// The prompt the init command runs. /// ///
/// @@ -30413,17 +33854,12 @@ pub struct SandboxProxyCaTrustResult { ///
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct SandboxProxyCaRemoveResult { - /// Whether this process can add the certificate authority to OS trust without credentials from a different user. False where OS trust is unsupported, and on Windows when the process cannot elevate itself to write the machine trust store. When false, do not offer to set up the certificate authority. - pub can_install: bool, - /// Human-readable reason for the state. On `installed` or `notInstalled`, present only when the certificate authority must be rotated, and then says why. - #[serde(skip_serializing_if = "Option::is_none")] - pub detail: Option, - /// The state of the certificate authority. - pub state: SandboxProxyCaState, +pub(crate) struct InstructionsGetInitPromptResult { + /// Prompt text authoring repository instructions. + pub prompt: String, } -/// Built-in tools available for the requested model, with their parameters and instructions. +/// Instruction sources discovered across user, repository, and plugin sources. /// ///
/// @@ -30433,12 +33869,12 @@ pub struct SandboxProxyCaRemoveResult { ///
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct ToolsListResult { - /// List of available built-in tools with metadata - pub tools: Vec, +pub struct InstructionsDiscoverResult { + /// All discovered instruction sources + pub sources: Vec, } -/// User-configured MCP servers, keyed by server name. +/// Instruction sources discovered across user, repository, and plugin sources. /// ///
/// @@ -30448,12 +33884,12 @@ pub struct ToolsListResult { ///
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct McpConfigListResult { - /// All MCP servers from user config, keyed by name - pub servers: HashMap, +pub struct InstructionsDiscoverRepoSourcesResult { + /// All discovered instruction sources + pub sources: Vec, } -/// Extensions discovered from persisted Copilot home state and their effective loading mode. Launch-scoped additional plugins are not included. +/// Canonical files and directories where custom instructions can be created so the runtime will recognize them. /// ///
/// @@ -30463,11 +33899,9 @@ pub struct McpConfigListResult { ///
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct ExtensionsDiscoverResult { - /// Discovered user and enabled installed-plugin extensions from persisted Copilot home state - pub extensions: Vec, - /// Effective extension loading mode. Defaults to load_and_augment when unset. - pub mode: DiscoveredExtensionMode, +pub struct InstructionsGetDiscoveryPathsResult { + /// Canonical instruction create/discovery files and directories, in priority order + pub paths: Vec, } /// Skills discovered across global and project sources. @@ -30688,36 +34122,6 @@ pub struct AgentsGetDiscoveryPathsResult { pub paths: Vec, } -/// Instruction sources discovered across user, repository, and plugin sources. -/// -///
-/// -/// **Experimental.** This type is part of an experimental wire-protocol surface -/// and may change or be removed in future SDK or CLI releases. -/// -///
-#[derive(Debug, Clone, Default, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct InstructionsDiscoverResult { - /// All discovered instruction sources - pub sources: Vec, -} - -/// Canonical files and directories where custom instructions can be created so the runtime will recognize them. -/// -///
-/// -/// **Experimental.** This type is part of an experimental wire-protocol surface -/// and may change or be removed in future SDK or CLI releases. -/// -///
-#[derive(Debug, Clone, Default, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct InstructionsGetDiscoveryPathsResult { - /// Canonical instruction create/discovery files and directories, in priority order - pub paths: Vec, -} - /// The host's machine-wide state. Every field is optional because a fresh install has recorded nothing yet, so a reader must treat an absent field as `not yet`, never as a negative answer. Stored credentials are deliberately absent from this shape. /// ///
@@ -30729,9 +34133,6 @@ pub struct InstructionsGetDiscoveryPathsResult { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub(crate) struct GlobalStateLoadForConfigDirResult { - /// Whether the user has answered the prompt suggesting they install the desktop app. - #[serde(skip_serializing_if = "Option::is_none")] - pub app_install_nudge_responded: Option, /// Whether the app tip has been shown. #[serde(skip_serializing_if = "Option::is_none")] pub app_tip_shown: Option, @@ -31397,6 +34798,36 @@ pub struct SessionSandboxGetEnforcementStatusResult { pub required: bool, } +/// Identifies the target session. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SessionSandboxGetCredentialSuggestionsParams { + /// Target session identifier + pub session_id: SessionId, +} + +/// Possible unconfigured secrets in the sandbox shell environment, sorted by variable name, with no secret values. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SessionSandboxGetCredentialSuggestionsResult { + /// Candidates for user review. Empty when sandboxing is disabled or no unconfigured candidates are present. + pub suggestions: Vec, +} + /// Result of attempting to disable sandboxing for the current session. /// ///
@@ -32429,6 +35860,36 @@ pub struct SessionModelSetReasoningEffortResult { pub reasoning_effort: String, } +/// Reasoning-summaries presentation policy for the session's current model. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SessionModelGetReasoningSummariesResult { + /// Whether the supplied `persistedShowReasoning` setting only restates the model default. This reports a redundancy, not a standing instruction to delete the setting: it stays true for as long as the setting and the model default agree, including for a user who later chose that value deliberately. It exists for a one-time migration of a legacy setting written before the model default made it redundant, so a host that acts on it owns the cleanup and must gate it behind its own persisted `done` marker, delete only a setting whose value still matches the one it read, and never repeat the deletion on a later launch. Under an off-by-default reasoning-summaries experiment an explicit `showReasoning: true` opt-in also reads as redundant on a summary-producing model, so an ungated host would keep deleting that opt-in and hide summaries again on every launch. + pub clear_redundant_setting: bool, + /// Whether the session's reasoning-summaries experiment assignment has settled. False means this read does not wait for a pending assignment and reports the experiment-derived fields conservatively: `showHiddenHint` is false and `visibility` falls back to `persistedShowReasoning`, or is omitted when none was supplied. A host that wants the assigned answer re-reads until this is true. + pub experiment_settled: bool, + /// Whether the selected model shows reasoning summaries by default. + pub model_default_shows_summaries: bool, + /// False until the session has a model catalog and a selected model. This read never resolves the catalog itself, and the runtime emits no readiness notification, so repeating this call alone leaves `ready` false indefinitely on a session that has neither listed models nor run a turn. `session.model.list` is the prerequisite and the signal: it resolves and caches the catalog for the session, so a successful return means the next read is ready. The exception is a session that resolves models without a Copilot catalog, that is a BYOK registry or an alternate provider: `session.model.list` returns an empty list without caching a catalog, so `ready` stays false and this policy is unavailable there. While false, every model-policy field is false and `visibility` is omitted; `experimentSettled` is independent of readiness and still reports the assignment state. + pub ready: bool, + /// Whether turns on this session produce reasoning summaries, honouring an explicit session reasoning-summary override. + pub session_shows_summaries: bool, + /// Whether to show the hint that reasoning summaries are hidden. Always false while `experimentSettled` is false. + pub show_hidden_hint: bool, + /// Whether the selected model emits reasoning headers. + pub show_reasoning_headers: bool, + /// Default reasoning visibility, and only meaningful once `ready` is true: false when the session produces no summaries, else the persisted setting, else the experiment's default. While `ready` is true it is omitted only when all three are unavailable, that is when the session produces summaries, no `persistedShowReasoning` was supplied, and `experimentSettled` is false. While `ready` is false there is no model policy to report and this field is always omitted, so an absent value alone does not imply a pending experiment. + #[serde(skip_serializing_if = "Option::is_none")] + pub visibility: Option, +} + /// The list of models available to this session. /// ///
@@ -34846,20 +38307,23 @@ pub struct SessionManagedSettingsGetParams { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SessionManagedSettingsGetResult { - /// Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers, and forced on when `failClosed` is true. + /// Whether an explicit enterprise policy restriction disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers; an unresolved policy does not force this on by itself. pub bypass_permissions_disabled: bool, /// Whether a session-local permissions layer injected by the SDK host was present #[serde(skip_serializing_if = "Option::is_none")] pub client_managed: Option, /// Whether an actual device MDM/plist/registry/file managed-settings layer was present pub device_managed: bool, - /// Whether managed policy could not be determined (e.g. a failed server fetch) and the session fell back to the fail-closed restriction. When true, restrictions such as disabling bypass-permissions are enforced even though `settings` may be absent. + /// Whether managed policy could not be determined (e.g. a failed server fetch) and unresolved-policy safeguards remain active. This does not by itself disable bypass-permissions; `bypassPermissionsDisabled` reports only an explicit policy restriction. pub fail_closed: bool, /// The setting keys under enterprise management in the effective managed settings (e.g. `model`, `enabledPlugins`, `permissions`). Empty when no managed settings are in force. pub managed_keys: Vec, /// Whether at least two managed sources supplied permission allowlists, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. #[serde(skip_serializing_if = "Option::is_none")] pub permissions_allow_intersected: Option, + /// Reusable retained managed permission policy for managedSettings.permissions.evaluate, including source composition and session-local injection. Provided by live session snapshots; absent in older events. Refresh the context when managed policy changes. + #[serde(skip_serializing_if = "Option::is_none")] + pub permissions_context: Option, /// Whether the policy-helper managed-settings layer was present. The policy helper is the weakest channel: it fills keys no enterprise source set and can never replace one. #[serde(skip_serializing_if = "Option::is_none")] pub policy_helper_managed: Option, @@ -37947,6 +41411,126 @@ pub type SessionsGetClientMetadataResult = Vec; ///
pub type UIElicitationResponseContent = HashMap; +/// True when this process can offer installing the sandbox credential-proxy certificate authority into OS trust. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+pub type SandboxProxyCaCheckInstallationSupportResult = bool; + +/// True when the sandbox configuration uses the credential proxy and can need certificate-authority setup. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+pub type SandboxProxyCaCheckRelevanceResult = bool; + +/// The effective sandbox settings, with every unset value resolved to its default. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+pub type SandboxSettingsApplyDefaultsResult = HashMap; + +/// True when an OS-level sandbox can run on this host. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+pub type SandboxCheckPlatformSupportResult = bool; + +/// True when the sandbox on this host can reach host loopback directly. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+pub type SandboxCheckWindowsHostLoopbackSupportResult = bool; + +/// The user-facing refusal, including the platform remedy and the documentation link. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+pub type SandboxGetUnsupportedMessageResult = String; + +/// The user-facing notice that a saved sandbox setting is inert for this session. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+pub type SandboxGetUnsupportedSettingDisabledNoticeResult = String; + +/// True when the command stays available while enforcement is blocked. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+pub type SandboxCheckEnforcementCommandResult = bool; + +/// The stored user settings document, keyed by setting name. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+pub type UserSettingsLoadForConfigDirResult = HashMap; + +/// The effective runtime settings, keyed by setting name. Keys this runtime does not recognize are preserved rather than dropped, so a caller reading a document written by a newer runtime keeps every value. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+pub type UserSettingsBuildRuntimeResult = HashMap; + +/// The resolved gates, keyed by flag name. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+pub type FeatureFlagsResolveStartupGatesResult = HashMap; + +/// The resolved feature flags, keyed by flag name. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+pub type FeatureFlagsResolveResult = HashMap; + /// List of all authenticated users /// ///
@@ -38172,6 +41756,49 @@ pub enum AuthInfo { ApiKey(ApiKeyAuthInfo), } +/// Authentication type +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum AuthInfoType { + /// Authentication provided by a GitHub App HMAC credential. + #[serde(rename = "hmac")] + Hmac, + /// Authentication resolved from environment-provided credentials. + #[serde(rename = "env")] + Env, + /// Authentication from an interactive user sign-in. + #[serde(rename = "user")] + User, + /// Authentication from a selected provider-owned account, without a GitHub credential. + #[serde(rename = "account")] + Account, + /// Authentication delegated to the GitHub CLI. + #[serde(rename = "gh-cli")] + GhCli, + /// Authentication from an API key credential. + #[serde(rename = "api-key")] + ApiKey, + /// Authentication from a GitHub token. + #[serde(rename = "token")] + Token, + /// Authentication from an SDK GitHub token callback. + #[serde(rename = "token-provider")] + TokenProvider, + /// Authentication from a Copilot API token. + #[serde(rename = "copilot-api-token")] + CopilotApiToken, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// The provider kind stamped on a signed-in account. /// ///
@@ -38855,49 +42482,6 @@ pub enum AuthEnumerateValue { Providers(AuthEnumerateValueProviders), } -/// Authentication type -/// -///
-/// -/// **Experimental.** This type is part of an experimental wire-protocol surface -/// and may change or be removed in future SDK or CLI releases. -/// -///
-#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub enum AuthInfoType { - /// Authentication provided by a GitHub App HMAC credential. - #[serde(rename = "hmac")] - Hmac, - /// Authentication resolved from environment-provided credentials. - #[serde(rename = "env")] - Env, - /// Authentication from an interactive user sign-in. - #[serde(rename = "user")] - User, - /// Authentication from a selected provider-owned account, without a GitHub credential. - #[serde(rename = "account")] - Account, - /// Authentication delegated to the GitHub CLI. - #[serde(rename = "gh-cli")] - GhCli, - /// Authentication from an API key credential. - #[serde(rename = "api-key")] - ApiKey, - /// Authentication from a GitHub token. - #[serde(rename = "token")] - Token, - /// Authentication from an SDK GitHub token callback. - #[serde(rename = "token-provider")] - TokenProvider, - /// Authentication from a Copilot API token. - #[serde(rename = "copilot-api-token")] - CopilotApiToken, - /// Unknown variant for forward compatibility. - #[default] - #[serde(other)] - Unknown, -} - /// Login flow step variant discriminator. #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum AuthLoginStepOpenUrlKind { @@ -41634,6 +45218,49 @@ pub enum ExternalToolTextResultForLlmContentTextType { Text, } +/// The population a feature flag is declared for, before overrides. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum FeatureFlagAvailability { + /// Enabled for everyone, before any override. + #[serde(rename = "on")] + On, + /// Enabled only for accounts on the owning team. + #[serde(rename = "team")] + Team, + /// Enabled only for staff accounts that have also opted into experimental features. + #[serde(rename = "staff-and-experimental")] + StaffAndExperimental, + /// Enabled for staff accounts and for accounts that opted into experimental features. + #[serde(rename = "staff-or-experimental")] + StaffOrExperimental, + /// Enabled for any staff account, GitHub or Microsoft. + #[serde(rename = "staff")] + Staff, + /// Enabled for GitHub staff accounts only. + #[serde(rename = "staff-github")] + StaffGitHub, + /// Enabled for Microsoft staff accounts only. + #[serde(rename = "staff-microsoft")] + StaffMicrosoft, + /// Enabled only for accounts that opted into experimental features. + #[serde(rename = "experimental")] + Experimental, + /// Disabled for everyone, before any override. + #[serde(rename = "off")] + Off, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Why the runtime is requesting a GitHub credential. #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum GitHubTokenAcquireReason { @@ -42544,6 +46171,126 @@ pub enum SessionLogLevel { Unknown, } +/// Operation to evaluate. Currently only url is supported. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ManagedPermissionOperationKind { + #[serde(rename = "url")] + #[default] + Url, +} + +/// Managed-policy verdict only: deny blocks the URL, ask requires approval, allow approves under managed policy, and unmanaged leaves the normal consumer permission flow in effect. No verdict bypasses other security controls. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ManagedPermissionVerdict { + /// Managed policy prohibits the operation; do not offer approval. + #[serde(rename = "deny")] + Deny, + /// Managed policy requires the consumer to obtain approval. + #[serde(rename = "ask")] + Ask, + /// Managed policy allows the operation, subject to other security controls. + #[serde(rename = "allow")] + Allow, + /// Managed policy does not decide the operation; use the consumer's normal permission flow. + #[serde(rename = "unmanaged")] + Unmanaged, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// What retrying did for one plugin required by managed settings. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ManagedPluginRetryStatus { + /// The plugin was missing and is now installed. + #[serde(rename = "installed")] + Installed, + /// An update was available and is now installed. + #[serde(rename = "updated")] + Updated, + /// The plugin was already installed and current, so nothing was done. + #[serde(rename = "already_present")] + AlreadyPresent, + /// Checking, installing, or updating the plugin failed. See `error`. + #[serde(rename = "failed")] + Failed, + /// The plugin was not prepared because another session was changing plugins or the managed settings changed during the retry. It is prepared again before a later message. + #[serde(rename = "deferred")] + Deferred, + /// The session's managed settings do not require this plugin, so nothing was done. + #[serde(rename = "not_required")] + NotRequired, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// Which layer disabled bypass-permissions mode. The layer matters because only a server-delivered disable is pinned to an account and can later be lifted by signing in elsewhere. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ManagedSettingsBypassDisableSource { + /// The user turned bypass mode off themselves. + #[serde(rename = "user")] + User, + /// The account's server-delivered org policy disabled it. Pinned to the account named by `currentManagedIdentity`. + #[serde(rename = "managed")] + Managed, + /// The device/MDM layer disabled it. Outranks the account, so no server answer lifts it. + #[serde(rename = "mdm")] + Mdm, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// What the managed-policy resolution said about bypass-permissions mode. `unknown` is a third answer rather than a missing one, and the caller must send it explicitly when it has no verdict. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ManagedSettingsBypassPolicyResolution { + /// The policy resolved and does not disable bypass mode. + #[serde(rename = "allowed")] + Allowed, + /// The policy resolved and disables bypass mode. + #[serde(rename = "disabled")] + Disabled, + /// No verdict: the fetch failed, or the service declared a policy this build may not enforce. Whatever is in force is preserved, because reverting to permissive on an unreadable policy would widen access. + #[serde(rename = "unknown")] + UnknownValue, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// A channel accepted by managedSettings.compose. /// ///
@@ -42591,6 +46338,143 @@ pub enum ManagedSettingsDiagnosticSeverity { Unknown, } +/// The operation that requires a policy-helper response. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ManagedSettingsPolicyHelperReason { + /// Resolve policy for session startup. + #[serde(rename = "sessionStart")] + SessionStart, + /// Resolve policy before plugin activation. + #[serde(rename = "pluginActivation")] + PluginActivation, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// How an ingestion may use the on-disk server-policy cache. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ManagedSettingsServerPolicyCacheMode { + /// Serve a recent cache entry before a live fetch. + #[serde(rename = "preferFresh")] + PreferFresh, + /// Read cached policy without a network request. + #[serde(rename = "cacheOnly")] + CacheOnly, + /// Serve a cached entry only as a fallback after a failed live fetch. + #[serde(rename = "fallbackOnly")] + FallbackOnly, + /// Ignore the cache entirely and rely on the live fetch alone. + #[serde(rename = "disabled")] + Disabled, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// Severity of a diagnostic emitted while ingesting managed settings. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ManagedSettingsLogLevel { + /// Detail useful only when diagnosing an ingestion. + #[serde(rename = "debug")] + Debug, + /// Ordinary progress worth surfacing under a verbose flag. + #[serde(rename = "info")] + Info, + /// A condition the operator should see but which did not stop the ingestion. + #[serde(rename = "warning")] + Warning, + /// A failure that changed the outcome of the ingestion. + #[serde(rename = "error")] + Error, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// Which channel registered the policy helper that produced a helper layer. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ManagedSettingsPolicyHelperAuthority { + /// The helper was registered by the device (MDM or managed file) layer. + #[serde(rename = "device")] + Device, + /// The helper was registered by the account's server-delivered policy. + #[serde(rename = "server")] + Server, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// How the server layer of an ingestion was resolved. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ManagedSettingsServerPolicyResolution { + /// A live fetch succeeded. + #[serde(rename = "live")] + Live, + /// A recent cached entry was served without a live fetch. + #[serde(rename = "freshCache")] + FreshCache, + /// No cached policy or confirmed no-policy result exists. + #[serde(rename = "cacheMiss")] + CacheMiss, + /// A live fetch failed and a cached entry was served in its place. + #[serde(rename = "fallbackCache")] + FallbackCache, + /// No policy could be resolved and the host proceeds unmanaged. + #[serde(rename = "failOpen")] + FailOpen, + /// No policy could be resolved and the host applies the fail-closed gates. + #[serde(rename = "failClosed")] + FailClosed, + /// No fetch was attempted because no usable token was available. + #[serde(rename = "skippedNoToken")] + SkippedNoToken, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Allowed values for the `McpAppsHostContextDetailsAvailableDisplayMode` enumeration. /// ///
@@ -43715,6 +47599,28 @@ pub enum McpSetEnvValueModeDetails { Unknown, } +/// Controls how availableTools (allowlist) and excludedTools (denylist) combine when both are set. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum OptionsUpdateToolFilterPrecedence { + /// If availableTools is set, it is the only constraint that applies (excludedTools is ignored). Preserves CLI / pre-existing client behavior. Default. + #[serde(rename = "available")] + Available, + /// A tool is enabled if and only if it matches the allowlist (or the allowlist is unset) AND it does not match the denylist. Makes 'all except X' expressible by combining the two lists. + #[serde(rename = "excluded")] + Excluded, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Hosting platform type of the repository /// ///
@@ -44228,28 +48134,6 @@ pub enum OptionsUpdateReasoningSummary { Unknown, } -/// Controls how availableTools (allowlist) and excludedTools (denylist) combine when both are set. -/// -///
-/// -/// **Experimental.** This type is part of an experimental wire-protocol surface -/// and may change or be removed in future SDK or CLI releases. -/// -///
-#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub enum OptionsUpdateToolFilterPrecedence { - /// If availableTools is set, it is the only constraint that applies (excludedTools is ignored). Preserves CLI / pre-existing client behavior. Default. - #[serde(rename = "available")] - Available, - /// A tool is enabled if and only if it matches the allowlist (or the allowlist is unset) AND it does not match the denylist. Makes 'all except X' expressible by combining the two lists. - #[serde(rename = "excluded")] - Excluded, - /// Unknown variant for forward compatibility. - #[default] - #[serde(other)] - Unknown, -} - /// Approve this single request only #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum PermissionDecisionApproveOnceKind { @@ -45389,6 +49273,56 @@ pub enum RemoteSessionMetadataTaskType { Unknown, } +/// One atomic settings mutation. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum SettingsEditOperation { + /// Set one scalar value. + #[serde(rename = "set")] + Set, + /// Add one string to a list, unless present. + #[serde(rename = "add-item")] + AddItem, + /// Remove one string from a list, unless absent. + #[serde(rename = "remove-item")] + RemoveItem, + /// Remove the setting without supplying a value. + #[serde(rename = "remove")] + Remove, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// Which repository settings file to edit. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum RepoSettingsEditScope { + /// The shared repository settings file. + #[serde(rename = "repo")] + Repo, + /// The local repository settings file. + #[serde(rename = "local")] + Local, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Output format discriminator. Currently only json_schema is supported. #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum ResponseFormatType { @@ -46324,6 +50258,31 @@ pub enum SessionVisibilityStatus { Unknown, } +/// Whether one atomic settings edit changed a file. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum SettingsEditStatus { + /// The edit changed the setting and was stored. + #[serde(rename = "changed")] + Changed, + /// The setting already had the requested state. + #[serde(rename = "unchanged")] + Unchanged, + /// The existing settings file could not be edited safely; nothing was written. + #[serde(rename = "invalid")] + Invalid, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Signal to send (default: SIGTERM) /// ///
@@ -46852,6 +50811,62 @@ pub enum SlashCommandInvocationResult { SetPlanModel(SlashCommandSetPlanModelResult), } +/// What the one-time staff log-level migration did. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum StaffLogLevelMigrationStatus { + /// The account is not a staff account, so the migration does not apply and nothing was recorded. + #[serde(rename = "ineligible")] + Ineligible, + /// The migration already ran; the stored completion timestamp is returned unchanged. + #[serde(rename = "current")] + Current, + /// The migration ran now. The persisted settings may have changed, so the host should adopt the returned settings when they are present. + #[serde(rename = "refreshed")] + Refreshed, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// What the one-time staff update-channel migration did. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum StaffUpdateChannelMigrationStatus { + /// The account is not a staff account, so the migration does not apply and nothing was recorded. + #[serde(rename = "ineligible")] + Ineligible, + /// The migration already ran within the cutoff window; the stored completion timestamp is returned unchanged. + #[serde(rename = "current")] + Current, + /// The persisted update channel was changed to stable. Only this outcome requires the host to reload its configuration. + #[serde(rename = "migrated")] + Migrated, + /// The persisted channel was already stable, so nothing changed, but the migration is now recorded as done. + #[serde(rename = "completed")] + Completed, + /// The account is on the experimental channel, which the migration deliberately leaves alone, and the migration is recorded as done. + #[serde(rename = "excluded-experimental")] + ExcludedExperimental, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Context tier override for matching subagents /// ///
@@ -47435,6 +51450,34 @@ pub enum UISessionLimitsExhaustedResponseAction { Unknown, } +/// What the one-time obsolete-settings cleanup did to the document. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum UserSettingsMigrateObsoleteOnDiskStatus { + /// No settings document exists, so there was nothing to clean. + #[serde(rename = "missing")] + Missing, + /// The document could not be parsed, so it was left untouched rather than rewritten from a partial read. + #[serde(rename = "invalid")] + Invalid, + /// The document parsed and held nothing obsolete, so nothing was written. + #[serde(rename = "unchanged")] + Unchanged, + /// Obsolete keys were removed and the document was rewritten. + #[serde(rename = "changed")] + Changed, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Execution-critical workflow storage operation. /// ///
diff --git a/rust/src/generated/rpc.rs b/rust/src/generated/rpc.rs index 2eb9168bab..e085bf03ae 100644 --- a/rust/src/generated/rpc.rs +++ b/rust/src/generated/rpc.rs @@ -78,6 +78,13 @@ impl<'a> ClientRpc<'a> { } } + /// `featureFlags.*` sub-namespace. + pub fn feature_flags(&self) -> ClientRpcFeatureFlags<'a> { + ClientRpcFeatureFlags { + client: self.client, + } + } + /// `git.*` sub-namespace. pub fn git(&self) -> ClientRpcGit<'a> { ClientRpcGit { @@ -155,6 +162,20 @@ impl<'a> ClientRpc<'a> { } } + /// `permissions.*` sub-namespace. + pub fn permissions(&self) -> ClientRpcPermissions<'a> { + ClientRpcPermissions { + client: self.client, + } + } + + /// `persistence.*` sub-namespace. + pub fn persistence(&self) -> ClientRpcPersistence<'a> { + ClientRpcPersistence { + client: self.client, + } + } + /// `plugins.*` sub-namespace. pub fn plugins(&self) -> ClientRpcPlugins<'a> { ClientRpcPlugins { @@ -162,6 +183,13 @@ impl<'a> ClientRpc<'a> { } } + /// `repo.*` sub-namespace. + pub fn repo(&self) -> ClientRpcRepo<'a> { + ClientRpcRepo { + client: self.client, + } + } + /// `runtime.*` sub-namespace. pub fn runtime(&self) -> ClientRpcRuntime<'a> { ClientRpcRuntime { @@ -463,6 +491,123 @@ impl<'a> ClientRpcAccount<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + /// Renders the sign-in banner and the one-line account description the runtime uses for an authentication identity. The runtime words these in several places for its own callers, so a host that announces a sign-in or an account switch should render them from here rather than composing its own, which would let the two spellings drift apart. + /// + /// Wire method: `account.describeAuthInfo`. + /// + /// # Parameters + /// + /// * `params` - The authentication identity whose shared wording should be rendered. + /// + /// # Returns + /// + /// The wording the runtime uses for an authentication identity. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn describe_auth_info( + &self, + params: AccountDescribeAuthInfoRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::ACCOUNT_DESCRIBEAUTHINFO, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Reports whether the supplied identity can create gists. Gists are unavailable on GitHub Enterprise Cloud with data residency, and to Enterprise Managed Users on github.com, so a host that offers gist actions should hide or disable them when this reports no support. + /// + /// Wire method: `account.getGistSupport`. + /// + /// # Parameters + /// + /// * `params` - The authentication identity whose gist support should be reported. + /// + /// # Returns + /// + /// Whether the identity can create gists, and why not when it cannot. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn get_gist_support( + &self, + params: AccountGetGistSupportRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::ACCOUNT_GETGISTSUPPORT, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Resolves a path on the identity's GitHub host into an absolute URL, so a host can link to settings or documentation on the enterprise instance the user is signed in to rather than to github.com. Falls back to the public host when no identity is supplied. + /// + /// Wire method: `account.getWebUrl`. + /// + /// # Parameters + /// + /// * `params` - A path to resolve against the GitHub host an identity is signed in to. + /// + /// # Returns + /// + /// An absolute URL on the identity's GitHub host. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn get_web_url( + &self, + params: AccountGetWebUrlRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::ACCOUNT_GETWEBURL, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Names the environment variable supplying a classic personal access token, if one is set. A classic token cannot be upgraded in place, so a host that finds one should tell the user which variable to unset rather than starting an interactive sign-in that the variable would override. + /// + /// Wire method: `account.getTokenEnvVar`. + /// + /// # Returns + /// + /// The environment variable supplying a classic personal access token, when one is set. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn get_token_env_var(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::ACCOUNT_GETTOKENENVVAR, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `agentRegistry.*` RPCs. @@ -539,6 +684,39 @@ impl<'a> ClientRpcAgents<'a> { Ok(serde_json::from_value(_value)?) } + /// Discovers custom agents across user, project, plugin, and remote sources. + /// + /// Wire method: `agents.discover`. + /// + /// # Parameters + /// + /// * `params` - Optional project paths to include in agent discovery. + /// + /// # Returns + /// + /// Agents discovered across user, project, plugin, and remote sources. + /// + ///
+ /// + /// **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. + /// + ///
+ /// + /// Accepts [`AgentsDiscoverOptions`], including inputs added after [`AgentsDiscoverRequest`]. + pub async fn discover_with_options( + &self, + params: AgentsDiscoverOptions, + ) -> 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`. @@ -570,6 +748,39 @@ impl<'a> ClientRpcAgents<'a> { 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`. + /// + /// # Parameters + /// + /// * `params` - Optional project paths to include when enumerating agent discovery directories. + /// + /// # Returns + /// + /// Canonical locations where custom agents can be created so the runtime will recognize them. + /// + ///
+ /// + /// **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. + /// + ///
+ /// + /// Accepts [`AgentsGetDiscoveryPathsOptions`], including inputs added after [`AgentsGetDiscoveryPathsRequest`]. + pub async fn get_discovery_paths_with_options( + &self, + params: AgentsGetDiscoveryPathsOptions, + ) -> 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)?) + } + /// Lists the agents this runtime ships, by name. A consumer separating shipped agents from ones the user or a plugin authored should compare against these names rather than against `AgentInfo.source`: an authored agent may carry the `builtin` source while not being one of these, and the runtime treats the two as separate questions. `disableableNames` is the subset a user may turn off, which a client needs to decide whether to offer a toggle. `yamlBasedNames` is the subset backed by a shipped YAML definition, which a client needs before asking the runtime to load one. /// /// Wire method: `agents.getBuiltins`. @@ -1156,6 +1367,103 @@ impl<'a> ClientRpcExtensions<'a> { } } +/// `featureFlags.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcFeatureFlags<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcFeatureFlags<'a> { + /// Resolves named gates against this installation's cached online-experiment assignment, for the account the runtime is already authenticated as. Unlike `featureFlags.resolve`, which layers static availability with the populations supplied, this reads the experiment assignment the runtime cached for its own current authentication, so a launch can honor a rollout before any session exists. The account is never taken from the request: naming one would read another account's cached assignments. The cached assignment answers immediately and any network refresh happens behind it, so this call does not wait on the network. Not available to an extension, which cannot carry the user's consent to read their assignment. + /// + /// Wire method: `featureFlags.resolveStartupGates`. + /// + /// # Parameters + /// + /// * `params` - Caller population and startup inputs to resolve named online-experiment gates for. Carries no account: the runtime resolves the assignment for the authentication it already holds. + /// + /// # Returns + /// + /// The resolved gates, keyed by flag name. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn resolve_startup_gates( + &self, + params: FeatureFlagsResolveStartupGatesRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::FEATUREFLAGS_RESOLVESTARTUPGATES, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Lists every feature flag this runtime declares, with the population each one is declared for, its experimental description, and the online-experiment flag it maps to. This is the static catalog: it describes how each flag is defined, not whether it is on for anyone. Use `featureFlags.resolve` for static values with caller overrides, not active runtime values or experiment assignments. + /// + /// Wire method: `featureFlags.listMetadata`. + /// + /// # Returns + /// + /// Every feature flag this runtime declares, with the shape each one was declared with. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn list_metadata(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::FEATUREFLAGS_LISTMETADATA, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Computes static feature flags from the supplied population and overrides without changing runtime state or verifying audience membership. Starts from declared availability, then applies direct feature-flag environment values, user settings, and explicit `flagOverrides`. It does not apply `COPILOT_EXP_*` experiment assignments, authenticated startup gates, or active runtime state. Settings include `enabledFeatureFlags`, the legacy `feature_flags.enabled` list, and supported convenience settings. Supply `baseFlags` to use an existing flag set instead of declared availability. Omit `isStaffGitHub` or `isStaffMicrosoft` when membership was never recorded: a legacy staff account then counts as both populations. + /// + /// Wire method: `featureFlags.resolve`. + /// + /// # Parameters + /// + /// * `params` - Caller population and overrides for static feature-flag resolution, independent of experiment assignments and active runtime state. + /// + /// # Returns + /// + /// The resolved feature flags, keyed by flag name. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn resolve( + &self, + params: FeatureFlagsResolveRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::FEATUREFLAGS_RESOLVE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } +} + /// `git.*` RPCs. #[derive(Clone, Copy)] pub struct ClientRpcGit<'a> { @@ -1452,7 +1760,7 @@ impl<'a> ClientRpcGlobalState<'a> { Ok(serde_json::from_value(_value)?) } - /// Records one top-level key in the host's machine-wide state, the counterpart to `globalState.load`. A host calls this to remember that it has shown an onboarding step, asked a one-off question, or completed a migration, so the next run can skip it. Only the named key is replaced and the rest of the document is preserved, which lets two writers record different flags without overwriting each other; passing no value removes the key instead. Only the keys a host records itself are writable: `appInstallNudgeResponded`, `appTipShown`, `askedSetupTerminals`, `autoFeedbackLastPromptedAt`, `firstLaunchAt`, `recentModelIds`, `sandboxCredentialProxyCaDeclined` and `sandboxOnboardingShown`. Every other key is refused, including `installedPlugins`, the stored credentials, `trustedFolders`, the staff flags and the signed-in accounts. Plugin enablement must use the plugin APIs, which apply repository and managed-policy checks. + /// Records one top-level key in the host's machine-wide state, the counterpart to `globalState.load`. A host calls this to remember that it has shown an onboarding step, asked a one-off question, or completed a migration, so the next run can skip it. Only the named key is replaced and the rest of the document is preserved, which lets two writers record different flags without overwriting each other; passing no value removes the key instead. Only the keys a host records itself are writable: `appTipShown`, `askedSetupTerminals`, `autoFeedbackLastPromptedAt`, `firstLaunchAt`, `recentModelIds`, `sandboxCredentialProxyCaDeclined` and `sandboxOnboardingShown`. Every other key is refused, including `installedPlugins`, the stored credentials, `trustedFolders`, the staff flags and the signed-in accounts. Plugin enablement must use the plugin APIs, which apply repository and managed-policy checks. /// /// Wire method: `globalState.writeKey`. /// @@ -1514,26 +1822,18 @@ impl<'a> ClientRpcHooks<'a> { .await?; Ok(serde_json::from_value(_value)?) } -} - -/// `host.*` RPCs. -#[derive(Clone, Copy)] -pub struct ClientRpcHost<'a> { - pub(crate) client: &'a Client, -} -impl<'a> ClientRpcHost<'a> { - /// Publishes an attached resident session for this listener's lifetime without copying it. + /// Discovers hook actions enabled under server-side discovery settings from user, repository, plugin, and managed-policy sources. /// - /// Wire method: `host.publishSession`. + /// Wire method: `hooks.discover`. /// /// # Parameters /// - /// * `params` - Publishes a resident session attached to the listener's owning connection. + /// * `params` - Optional project paths and host-exclusion behavior for server-scoped hook discovery. /// /// # Returns /// - /// The existing runtime identity and its resource on the listener. + /// Server-discovered hook actions and partial-load diagnostics from user, repository, plugin, and managed-policy sources. Concrete sessions may include additional session-specific hook sources. /// ///
/// @@ -1542,10 +1842,51 @@ impl<'a> ClientRpcHost<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn publish_session( + /// + /// Accepts [`HooksDiscoverOptions`], including inputs added after [`HooksDiscoverRequest`]. + pub async fn discover_with_options( &self, - params: HostPublishSessionRequest, - ) -> Result { + params: HooksDiscoverOptions, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::HOOKS_DISCOVER, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } +} + +/// `host.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcHost<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcHost<'a> { + /// Publishes an attached resident session for this listener's lifetime without copying it. + /// + /// Wire method: `host.publishSession`. + /// + /// # Parameters + /// + /// * `params` - Publishes a resident session attached to the listener's owning connection. + /// + /// # Returns + /// + /// The existing runtime identity and its resource on the listener. + /// + ///
+ /// + /// **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. + /// + ///
+ pub async fn publish_session( + &self, + params: HostPublishSessionRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client @@ -1825,6 +2166,30 @@ pub struct ClientRpcInstructions<'a> { } impl<'a> ClientRpcInstructions<'a> { + /// Returns the prompt the `init` command runs to author repository instructions. A host offering its own initialize action sends this text so the instructions it produces match the ones the CLI produces. The text is fixed for a build and depends on no configuration, session or repository. + /// + /// Wire method: `instructions.getInitPrompt`. + /// + /// # Returns + /// + /// The prompt the init command runs. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn get_init_prompt(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::INSTRUCTIONS_GETINITPROMPT, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Discovers instruction sources across user, repository, and plugin sources. /// /// Wire method: `instructions.discover`. @@ -1856,6 +2221,73 @@ impl<'a> ClientRpcInstructions<'a> { Ok(serde_json::from_value(_value)?) } + /// Discovers instruction sources across user, repository, and plugin sources. + /// + /// Wire method: `instructions.discover`. + /// + /// # Parameters + /// + /// * `params` - Optional project paths to include in instruction discovery. + /// + /// # Returns + /// + /// Instruction sources discovered across user, repository, and plugin sources. + /// + ///
+ /// + /// **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. + /// + ///
+ /// + /// Accepts [`InstructionsDiscoverOptions`], including inputs added after [`InstructionsDiscoverRequest`]. + pub async fn discover_with_options( + &self, + params: InstructionsDiscoverOptions, + ) -> 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)?) + } + + /// Discovers the instruction sources for one caller-named location and returns them in the order the runtime applies them. `discover` answers for a set of project paths and derives the rest of its scope itself; this one instead takes every argument from the caller — the extra directories to scan and whether to descend into nested instruction files — and honors them as given. It also includes instruction directories from trusted repository settings, matching native repository discovery. Use it when the caller knows its own scope and no session exists to derive that scope from, such as a host reporting how many instruction files a directory contributes before any session starts. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract; it is migration plumbing, not a surface consumers are meant to depend on. + /// + /// Wire method: `instructions.discoverRepoSources`. + /// + /// # Parameters + /// + /// * `params` - Explicit discovery arguments for one location, supplied by the caller rather than derived from a session. + /// + /// # Returns + /// + /// Instruction sources discovered across user, repository, and plugin sources. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn discover_repo_sources( + &self, + params: InstructionsDiscoverRepoSourcesRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::INSTRUCTIONS_DISCOVERREPOSOURCES, + 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`. @@ -1889,6 +2321,62 @@ impl<'a> ClientRpcInstructions<'a> { .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`. + /// + /// # Parameters + /// + /// * `params` - Optional project paths to include when enumerating instruction discovery targets. + /// + /// # Returns + /// + /// Canonical files and directories where custom instructions can be created so the runtime will recognize them. + /// + ///
+ /// + /// **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. + /// + ///
+ /// + /// Accepts [`InstructionsGetDiscoveryPathsOptions`], including inputs added after [`InstructionsGetDiscoveryPathsRequest`]. + pub async fn get_discovery_paths_with_options( + &self, + params: InstructionsGetDiscoveryPathsOptions, + ) -> 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)?) + } + + /// Drops the runtime's in-memory cache of discovered custom instructions so the next discovery reads them from disk again. A host calls this after it changes instruction files, or after it trusts a folder whose instructions an earlier discovery skipped, so that a session started afterwards sees the current files instead of a cached earlier answer. The call performs no I/O of its own and removes nothing from disk; it only invalidates, so the cost lands on the next discovery. Returns once the cache is clear, which is what lets a caller order a session start after it. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract; it is migration plumbing, not a surface consumers are meant to depend on. + /// + /// Wire method: `instructions.clearCache`. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn clear_cache(&self) -> Result<(), Error> { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::INSTRUCTIONS_CLEARCACHE, Some(wire_params)) + .await?; + Ok(()) + } } /// `llmInference.*` RPCs. @@ -1998,13 +2486,20 @@ pub struct ClientRpcManagedSettings<'a> { } impl<'a> ClientRpcManagedSettings<'a> { - /// Discovers device-managed settings from production MDM and managed-file sources, validates them against the runtime-owned managed-settings schema, and returns the canonical JSON without requiring a session. `managedSettings.resolve` returns the same device settings together with the account's server policy. + /// `managedSettings.permissions.*` sub-namespace. + pub fn permissions(&self) -> ClientRpcManagedSettingsPermissions<'a> { + ClientRpcManagedSettingsPermissions { + client: self.client, + } + } + + /// Discovers device-managed settings the way the runtime's own device layer does, and reports the sandbox floor and the load outcome alongside the validated document. This differs from `managedSettings.read` in three ways a policy-enforcing host depends on: a document that validates to an empty object is reported as absent rather than as an empty policy, a discovery error does not suppress a document that still validates, and a failure is reported as a flag rather than only as error text. Hosts that compose the device layer into an effective policy use this, because `managedSettings.read` cannot distinguish "no device policy" from "device policy could not be read" and would silently hand the user's own layer the win. /// - /// Wire method: `managedSettings.read`. + /// Wire method: `managedSettings.readDevice`. /// /// # Returns /// - /// Validated device-managed settings discovered before a session exists. + /// The device-managed settings layer, with the sandbox floor and the load outcome a host needs to compose it into an effective policy. /// ///
/// @@ -2013,18 +2508,22 @@ impl<'a> ClientRpcManagedSettings<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn read(&self) -> Result { + pub(crate) async fn read_device(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::MANAGEDSETTINGS_READ, Some(wire_params)) + .call(rpc_methods::MANAGEDSETTINGS_READDEVICE, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Force-refreshes enterprise managed settings for every account: wipes the persistent server-policy cache (the whole `/managed-settings` directory) and drops this runtime process's in-memory retained server policy. It does not itself fetch policy — the effect is that the next time a session resolves managed settings for an account, that resolution re-fetches the account's org policy from the network instead of serving a cached response. Note that `managedSettings.read` returns only device/MDM settings and never triggers the account server-policy fetch, so a host implementing "sync account policy" should call `managedSettings.resolve` or start a fresh session resolution rather than treat a subsequent `managedSettings.read` as the refreshed org policy. Mirrors the invalidation a sign-out performs, broadened from the one signing-out account to all of them; device/MDM layers describe the machine, not the account, and are left untouched. Rejects if the on-disk cache cannot be removed. + /// Retains the supplied managed layers as this runtime process's startup policy and re-applies the result to every live session. Hosts that own their own short-lived process policy use this, so a guarded mutation is checked against the policy the host actually fetched. Retaining is a whole-process replacement rather than a merge, so a host that already resolved a good policy must not retain a partial one over it. The failure flags are honest inputs rather than assumptions: reporting a fetch or load that failed keeps the layers already retained, and the interim fail-closed state, instead of clearing them. /// - /// Wire method: `managedSettings.clearCache`. + /// Wire method: `managedSettings.retainPolicy`. + /// + /// # Parameters + /// + /// * `params` - The managed layers to retain as this process's startup policy, with the outcome of fetching each one. /// ///
/// @@ -2033,22 +2532,29 @@ impl<'a> ClientRpcManagedSettings<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn clear_cache(&self) -> Result<(), Error> { - let wire_params = serde_json::json!({}); + pub(crate) async fn retain_policy( + &self, + params: ManagedSettingsRetainPolicyParams, + ) -> Result<(), Error> { + let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MANAGEDSETTINGS_CLEARCACHE, Some(wire_params)) + .call(rpc_methods::MANAGEDSETTINGS_RETAINPOLICY, Some(wire_params)) .await?; Ok(()) } - /// Resolves the effective enterprise managed settings without a session, from the device channel and, when an account is available, the account's server policy through the same per-account cache sessions use. A cached server policy less than an hour old is used without a fetch; otherwise the policy is fetched, and when the fetch fails a cached policy up to 24 hours old is used instead, unless `forceRemoteSettingsRefresh` requires a live fetch. With no account requested or signed in, it reports device policy only; signing out removes the account's cached policy. It can fetch server policy over the network when the cache is stale, so call it off latency-critical paths such as startup rather than before listing models. The policy helper is not run. `layers` lists each channel's document before merging, and `values` and `meta` carry typed effective values and their lock state for the keys typed so far. + /// Stamps an effective managed policy's individually-controlled entries onto a user settings document, returning the settings a host should actually act on. The managed layer wins on every plugin spec and marketplace name it names, and leaves everything it does not name as the user wrote it. Hosts use this rather than merging the two documents themselves, because the merge is per entry rather than per document and a whole-document overlay would either discard the user's unmanaged entries or let a user entry outrank a managed one. Omitting the managed layer returns the settings unchanged, which is what an unmanaged host wants. Rejects when either document is present but is not a JSON object rather than treating it as absent, since silently dropping a malformed policy hands the user's layer the win on exactly the entries the policy exists to control. /// - /// Wire method: `managedSettings.resolve`. + /// Wire method: `managedSettings.applyControls`. + /// + /// # Parameters + /// + /// * `params` - A user settings document and the effective managed policy whose controlled entries should be stamped onto it. /// /// # Returns /// - /// Effective enterprise managed settings for an account, resolved without a session. + /// The user settings with the managed layer's controlled entries stamped on. /// ///
/// @@ -2057,26 +2563,32 @@ impl<'a> ClientRpcManagedSettings<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn resolve(&self) -> Result { - let wire_params = serde_json::json!({}); + pub(crate) async fn apply_controls( + &self, + params: ManagedSettingsApplyControlsParams, + ) -> Result { + let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MANAGEDSETTINGS_RESOLVE, Some(wire_params)) + .call( + rpc_methods::MANAGEDSETTINGS_APPLYCONTROLS, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } - /// Resolves the effective enterprise managed settings without a session, from the device channel and, when an account is available, the account's server policy through the same per-account cache sessions use. A cached server policy less than an hour old is used without a fetch; otherwise the policy is fetched, and when the fetch fails a cached policy up to 24 hours old is used instead, unless `forceRemoteSettingsRefresh` requires a live fetch. With no account requested or signed in, it reports device policy only; signing out removes the account's cached policy. It can fetch server policy over the network when the cache is stale, so call it off latency-critical paths such as startup rather than before listing models. The policy helper is not run. `layers` lists each channel's document before merging, and `values` and `meta` carry typed effective values and their lock state for the keys typed so far. + /// Validates the managed settings a client is supplying for a session it is about to open, and returns them reduced to the keys a client is allowed to set. A client-supplied policy is not the same thing as an administrator's: it may only narrow permissions, so this accepts `permissions` and rejects every other key outright rather than ignoring it. Rejecting is the point — a host that silently dropped an unsupported key would report a policy as applied while running without it. Hosts call this before opening the session rather than letting the open fail, so a malformed policy is reported against the request that carried it. The runtime applies the same rule again when the session starts, so this cannot be used to smuggle a wider document past it. An accepted document that sets nothing comes back as an empty object, which is distinct from supplying nothing at all. /// - /// Wire method: `managedSettings.resolve`. + /// Wire method: `managedSettings.parseClientValue`. /// /// # Parameters /// - /// * `params` - Optional opaque account selection or GitHub token whose managed settings are resolved. + /// * `params` - The client-supplied managed settings to validate and normalize. Omitting them is valid and yields no policy. /// /// # Returns /// - /// Effective enterprise managed settings for an account, resolved without a session. + /// The accepted managed settings, reduced to the keys a client is allowed to set. /// ///
/// @@ -2085,25 +2597,32 @@ impl<'a> ClientRpcManagedSettings<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn resolve_with_params( + pub(crate) async fn parse_client_value( &self, - params: ManagedSettingsResolveRequest, - ) -> Result { + params: ManagedSettingsParseClientValueParams, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MANAGEDSETTINGS_RESOLVE, Some(wire_params)) + .call( + rpc_methods::MANAGEDSETTINGS_PARSECLIENTVALUE, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } - /// Returns the managed-settings authoring JSON schema with descriptive `x-composition` annotations aligned with the shared settings-engine vocabulary. These annotations are not a complete runtime composition contract: model, effortLevel, and contextTier remain coupled. Use `managedSettings.compose` for the runtime's effective result. Performs no I/O. + /// Composes the supplied server, device/MDM, device sandbox floor and policy-helper layers into the single effective managed policy a host should enforce, resolving each key across the layers by the runtime's own precedence rules. Hosts that assemble managed layers themselves use this so their precedence matches the runtime's exactly instead of reimplementing it. The composition is not a pure function of its inputs: an advisory sandbox force-on that arrived over the server channel is declined when this host cannot run a sandbox, so the result reflects the runtime host's capabilities, and the returned settings carry `sandboxAdvisoryFloorDeclined: true` when that happened. Enforced channels (device/MDM and policy helper) are never declined, and a server force-on carrying `failIfUnavailable` is preserved so the session fails loudly instead of quietly running unsandboxed. Rejects when a supplied layer is present but is not a JSON object, rather than treating it as unmanaged, because silently dropping a malformed policy would hand the user's own layer the win on exactly the keys the policy exists to control. /// - /// Wire method: `managedSettings.schema`. + /// Wire method: `managedSettings.resolveEffectiveValue`. + /// + /// # Parameters + /// + /// * `params` - The managed-settings layers to compose into an effective policy. Every layer is optional; omitting all of them yields no effective policy. /// /// # Returns /// - /// The authoring JSON schema for managed settings recognized by this runtime. + /// The effective managed policy composed from the supplied layers. /// ///
/// @@ -2112,26 +2631,32 @@ impl<'a> ClientRpcManagedSettings<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn schema(&self) -> Result { - let wire_params = serde_json::json!({}); + pub(crate) async fn resolve_effective_value( + &self, + params: ManagedSettingsResolveEffectiveValueParams, + ) -> Result { + let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MANAGEDSETTINGS_SCHEMA, Some(wire_params)) + .call( + rpc_methods::MANAGEDSETTINGS_RESOLVEEFFECTIVEVALUE, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } - /// Validates a candidate managed-settings document the way the runtime validates delivered policy, without applying it. Reports errors that would reject the document, warnings for content the runtime ignores, and the canonical document it would apply. Document text nested more than 64 levels deep is rejected. Performs no I/O. + /// Decides what a host must do to its bypass-permissions state after a managed-policy resolution lands, given the state already in force and the account now signed in. A host holds two pieces of state — which layer disabled bypass mode, and for a server-delivered disable, the account it was pinned to — and cannot decide the next step from the new policy alone. Three rules make that so, and each one is a control rather than a preference. A device/MDM disable outranks the account, so a later server answer never lifts it. An unknown policy, which is what a failed fetch or a refusal this build cannot enforce yields, preserves whatever is in force instead of reverting to permissive. And a server disable is lifted only when the account it was pinned to is no longer the signed-in one, which is what makes signing in to an unrestricted account recover without a restart while a reconnect on the same restricted account does not. Hosts call this rather than reimplementing it because getting the pinning backwards either strands a disable on an account that never carried one or drops one that still applies. `setDisabledState` is omitted when the host must leave its stored flag untouched, which is distinct from being told to set it to false. /// - /// Wire method: `managedSettings.validate`. + /// Wire method: `managedSettings.resolveBypassDisableTransition`. /// /// # Parameters /// - /// * `params` - A candidate managed-settings document to validate without applying it. + /// * `params` - The bypass-permissions state a host holds today, the account now signed in, and what the managed-policy resolution said. /// /// # Returns /// - /// Result of validating a managed-settings document. + /// The bypass-permissions state a host should hold after the resolution, and what it must do to get there. /// ///
/// @@ -2140,29 +2665,32 @@ impl<'a> ClientRpcManagedSettings<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn validate( + pub(crate) async fn resolve_bypass_disable_transition( &self, - params: ManagedSettingsValidateRequest, - ) -> Result { + params: ManagedSettingsResolveBypassDisableTransitionParams, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MANAGEDSETTINGS_VALIDATE, Some(wire_params)) + .call( + rpc_methods::MANAGEDSETTINGS_RESOLVEBYPASSDISABLETRANSITION, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } - /// Merges candidate managed-settings documents for the device, server, and policy-helper channels into the effective settings the runtime would enforce on this host, using the same precedence and composition rules as live resolution, without applying them. Like live resolution, a server's advisory sandbox force-enable is declined on a host that cannot run the sandbox. Does not fetch policy or read policy files, but may perform blocking OS or subprocess probes for sandbox support. Preview documents are limited to 1 MiB and 64 levels of nesting. + /// Resolves managed (enterprise policy) settings for a host from every layer at once: the account's server-delivered policy, the device/MDM layer, and a platform policy helper. Hosts use this instead of fetching and composing the layers themselves, because the per-layer precedence and the fail-closed rules that apply when a layer cannot be confirmed are the runtime's own and must match the policy a session will later enforce. It does not retain or apply anything to process-global policy state, so a host may call it before any session exists — which is the point, since plugin activation and one-shot commands resolve policy before a session is available to carry it. The returned per-layer outcome flags are load-bearing rather than diagnostic: a host must distinguish an unmanaged device from a managed device whose policy could not be confirmed, and apply its fail-closed gates in the second case. Reports its diagnostics in `logMessages` rather than logging them, so a host that runs before a log sink exists still sees them. /// - /// Wire method: `managedSettings.compose`. + /// Wire method: `managedSettings.ingest`. /// /// # Parameters /// - /// * `params` - Candidate managed-settings documents to merge without applying them. + /// * `params` - Inputs describing the host and account an ingestion should resolve managed policy for. /// /// # Returns /// - /// The effective managed settings the runtime would enforce for the given documents, in the same shape `managedSettings.resolve` returns. + /// The managed policy layers an ingestion resolved, together with the per-layer outcome flags a host needs to decide how much to trust the result. The flags are reported rather than folded into the settings because a host must be able to distinguish an unmanaged device from a managed device whose policy could not be confirmed, and apply its fail-closed gates in the second case. /// ///
/// @@ -2171,51 +2699,29 @@ impl<'a> ClientRpcManagedSettings<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn compose( + pub(crate) async fn ingest( &self, - params: ManagedSettingsComposeRequest, - ) -> Result { + params: ManagedSettingsIngestParams, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MANAGEDSETTINGS_COMPOSE, Some(wire_params)) + .call(rpc_methods::MANAGEDSETTINGS_INGEST, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } -} - -/// `mcp.*` RPCs. -#[derive(Clone, Copy)] -pub struct ClientRpcMcp<'a> { - pub(crate) client: &'a Client, -} -impl<'a> ClientRpcMcp<'a> { - /// `mcp.config.*` sub-namespace. - pub fn config(&self) -> ClientRpcMcpConfig<'a> { - ClientRpcMcpConfig { - client: self.client, - } - } - - /// `mcp.installations.*` sub-namespace. - pub fn installations(&self) -> ClientRpcMcpInstallations<'a> { - ClientRpcMcpInstallations { - client: self.client, - } - } - - /// Discovers MCP servers from user, workspace, plugin, and builtin sources. + /// Resolves the managed (enterprise policy) settings a host must enforce from startup, and judges the account against the enterprise sign-in pin in the same pass. This composes the layers the way `managedSettings.ingest` does and then answers the two questions a host cannot answer for itself: which fail-closed floors apply when a layer could not be confirmed, and whether this credential is allowed to sign in at all. The sign-in verdict is the reason a host must not assemble this itself from `managedSettings.ingest` — a pinned organization is an access control, and a host that composed the layers but skipped the pin would let a developer sign in with a personal or another enterprise's account and escape every control that keys off the signed-in identity. The verdict is computed on every resolution, including one that is not retained, so declining to apply policy never widens access. Set `retainAndApply` to keep the resolved policy as this runtime process's startup policy; leave it false to resolve without side effects. /// - /// Wire method: `mcp.discover`. + /// Wire method: `managedSettings.resolveStartupPolicy`. /// /// # Parameters /// - /// * `params` - Optional working directory used as context for MCP server discovery. + /// * `params` - Inputs describing the host and account a startup policy resolution should run for. Mirrors `managedSettings.ingest`, and adds the decision of whether the resolved policy is retained for this runtime process. /// /// # Returns /// - /// MCP servers discovered from user, workspace, plugin, and built-in sources. + /// The startup policy a resolution composed, the per-layer outcome flags a host needs to decide how much to trust it, and the enterprise sign-in verdict for the credential it ran against. The flags are reported rather than folded into the settings because a host must distinguish an unmanaged device from a managed device whose policy could not be confirmed, and apply its fail-closed gates in the second case. /// ///
/// @@ -2224,26 +2730,32 @@ impl<'a> ClientRpcMcp<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn discover(&self, params: McpDiscoverRequest) -> Result { + pub(crate) async fn resolve_startup_policy( + &self, + params: ManagedSettingsResolveStartupPolicyParams, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MCP_DISCOVER, Some(wire_params)) + .call( + rpc_methods::MANAGEDSETTINGS_RESOLVESTARTUPPOLICY, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } - /// Requests a side-effect-free MCP install plan from a catalog candidate handle or a caller-supplied card. This host-implemented server method is available through SDK/TUI hosts; standalone and C-ABI runtimes whose host does not implement server-method dispatch return JSON-RPC MethodNotFound. A runtime with planning available returns a normalised plan and opaque single-use plan handle; a runtime without it returns the typed planning-unavailable result. A completed plan reports resource identity, provenance, eligible transport choices, the user-scope target, required typed values and secret placeholders, the policy result, the configuration changes installing would make, and whether a reload would be needed. Planning never writes configuration, stores a secret, or reloads MCP servers, so abandoning a plan needs no call and leaves nothing behind. + /// Resolves the managed `remoteControl` policy for an account on its own, for a host deciding whether a remote-control session may start. Composes the same layers as `managedSettings.ingest` and returns only that one key, so a host does not have to know which layer won. An absent policy means the account is unmanaged for this capability and the host may proceed; that default is safe only while absence really does mean "no policy", so a service refusal that says an account policy exists which this client is too old to enforce resolves to an explicit disabled policy instead of to absence. A resolution that cannot be completed is rejected rather than answered as absence, for the same reason. /// - /// Wire method: `mcp.planInstall`. + /// Wire method: `managedSettings.loadRemoteControl`. /// /// # Parameters /// - /// * `params` - A side-effect-free request for an MCP install plan. Computing a plan never writes configuration, stores a secret, or reloads MCP servers. + /// * `params` - Inputs describing the host and account whose managed remote-control policy should be resolved. Mirrors `managedSettings.ingest`, and deliberately carries no retention decision: reading this gate never becomes this runtime process's startup policy. /// /// # Returns /// - /// Outcome of an mcp.planInstall call: either a normalised plan, or one typed refusal. Nothing is written in either case. + /// The managed remote-control policy in force for an account, if any. An absent policy means the account is unmanaged for this capability, which a host reads as allowed — so absence here is a positive statement that no policy applies, never a stand-in for a resolution that failed. A resolution that cannot be completed is rejected instead. /// ///
/// @@ -2252,29 +2764,32 @@ impl<'a> ClientRpcMcp<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn plan_install( + pub(crate) async fn load_remote_control( &self, - params: McpPlanInstallRequest, - ) -> Result { + params: ManagedSettingsLoadRemoteControlParams, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MCP_PLANINSTALL, Some(wire_params)) + .call( + rpc_methods::MANAGEDSETTINGS_LOADREMOTECONTROL, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } - /// Requests a side-effect-free MCP install plan from a catalog candidate handle or a caller-supplied card. This host-implemented server method is available through SDK/TUI hosts; standalone and C-ABI runtimes whose host does not implement server-method dispatch return JSON-RPC MethodNotFound. A runtime with planning available returns a normalised plan and opaque single-use plan handle; a runtime without it returns the typed planning-unavailable result. A completed plan reports resource identity, provenance, eligible transport choices, the user-scope target, required typed values and secret placeholders, the policy result, the configuration changes installing would make, and whether a reload would be needed. Planning never writes configuration, stores a secret, or reloads MCP servers, so abandoning a plan needs no call and leaves nothing behind. + /// Evaluates the device-channel enterprise sign-in pin (MDM plist, registry, or managed-settings file) against a credential, without resolving any server-delivered policy. This is the gate an entry point runs before it branches, and the only one that can refuse a developer's first sign-in: it is local, needs no credential, and a server-delivered pin only ever reaches accounts already inside the organization. It fails closed by construction — a discovery that cannot determine whether a pin exists refuses rather than allows, because a control that fails open under a triggerable condition is not a control. Hosts that also want the server channel should call `managedSettings.resolveStartupPolicy`, which folds both channels into one verdict. /// - /// Wire method: `mcp.planInstall`. + /// Wire method: `managedSettings.resolveDeviceForceLoginOrgsGate`. /// /// # Parameters /// - /// * `params` - A side-effect-free request for an MCP install plan. Computing a plan never writes configuration, stores a secret, or reloads MCP servers. + /// * `params` - The credential and environment to evaluate the device-channel sign-in pin against. /// /// # Returns /// - /// Outcome of an mcp.planInstall call: either a normalised plan, or one typed refusal. Nothing is written in either case. + /// The verdict of the `forceLoginOrgs` enterprise sign-in pin for one credential. A managed device may pin Copilot to an approved set of GitHub organizations so a developer cannot sign in with a personal — or another enterprise's — account and escape every control that keys off the signed-in identity. A host must refuse to proceed when `allowed` is false; the remaining fields explain the refusal rather than qualifying it. /// ///
/// @@ -2283,31 +2798,28 @@ impl<'a> ClientRpcMcp<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- /// - /// Accepts [`McpPlanInstallOptions`], including inputs added after [`McpPlanInstallRequest`]. - pub async fn plan_install_with_options( + pub(crate) async fn resolve_device_force_login_orgs_gate( &self, - params: McpPlanInstallOptions, - ) -> Result { + params: ManagedSettingsResolveDeviceForceLoginOrgsGateParams, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MCP_PLANINSTALL, Some(wire_params)) + .call( + rpc_methods::MANAGEDSETTINGS_RESOLVEDEVICEFORCELOGINORGSGATE, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } - /// Consumes a bound catalogue plan and retains one exact fully resolved personal remote MCP operation requiring no supplied values or configured secrets. Returns its runtime operation ID and original expiry before any confirmation, activation, writer initialisation or installation effect. Register the original connection, operation and selected-session binding before calling applyInstall. Missing lower owned admission is unavailable, never a raw-config fallback. - /// - /// Wire method: `mcp.prepareInstall`. - /// - /// # Parameters + /// Discovers device-managed settings from production MDM and managed-file sources, validates them against the runtime-owned managed-settings schema, and returns the canonical JSON without requiring a session. `managedSettings.resolve` returns the same device settings together with the account's server policy. /// - /// * `params` - Side-effect-free preparation of one original bound remote MCP choice. + /// Wire method: `managedSettings.read`. /// /// # Returns /// - /// Management result with contract receipt, or a typed request/negotiation refusal. + /// Validated device-managed settings discovered before a session exists. /// ///
/// @@ -2316,29 +2828,18 @@ impl<'a> ClientRpcMcp<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn prepare_install( - &self, - params: McpPrepareInstallRequest, - ) -> Result { - let wire_params = serde_json::to_value(params)?; + pub async fn read(&self) -> Result { + let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::MCP_PREPAREINSTALL, Some(wire_params)) + .call(rpc_methods::MANAGEDSETTINGS_READ, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Consumes a retained prepared MCP operation once, revalidates its original authority, requests explicit human consent through installations.confirm on the original connection, then revalidates source and applies the sealed transaction. An uncertain result requires original-operation inspection or recovery, never replay. - /// - /// Wire method: `mcp.applyInstall`. - /// - /// # Parameters - /// - /// * `params` - Applies exactly one previously prepared operation on its original connection. - /// - /// # Returns + /// Force-refreshes enterprise managed settings for every account: wipes the persistent server-policy cache (the whole `/managed-settings` directory) and drops this runtime process's in-memory retained server policy. It does not itself fetch policy — the effect is that the next time a session resolves managed settings for an account, that resolution re-fetches the account's org policy from the network instead of serving a cached response. Note that `managedSettings.read` returns only device/MDM settings and never triggers the account server-policy fetch, so a host implementing "sync account policy" should call `managedSettings.resolve` or start a fresh session resolution rather than treat a subsequent `managedSettings.read` as the refreshed org policy. Mirrors the invalidation a sign-out performs, broadened from the one signing-out account to all of them; device/MDM layers describe the machine, not the account, and are left untouched. Rejects if the on-disk cache cannot be removed. /// - /// An installation result together with the exact honoured contract, or a negotiation refusal. + /// Wire method: `managedSettings.clearCache`. /// ///
/// @@ -2347,29 +2848,22 @@ impl<'a> ClientRpcMcp<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn apply_install( - &self, - params: McpApplyInstallRequest, - ) -> Result { - let wire_params = serde_json::to_value(params)?; + pub async fn clear_cache(&self) -> Result<(), Error> { + let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::MCP_APPLYINSTALL, Some(wire_params)) + .call(rpc_methods::MANAGEDSETTINGS_CLEARCACHE, Some(wire_params)) .await?; - Ok(serde_json::from_value(_value)?) + Ok(()) } - /// Prepares a read-only removal plan for an exact owned receipt under the selected existing session. Returns the original operation ID before confirmation; neither planning nor abandonment changes configuration or shared OAuth credentials. - /// - /// Wire method: `mcp.planUninstall`. - /// - /// # Parameters + /// Resolves the effective enterprise managed settings without a session, from the device channel and, when an account is available, the account's server policy through the same per-account cache sessions use. A cached server policy less than an hour old is used without a fetch; otherwise the policy is fetched, and when the fetch fails a cached policy up to 24 hours old is used instead, unless `forceRemoteSettingsRefresh` requires a live fetch. With no account requested or signed in, it reports device policy only; signing out removes the account's cached policy. It can fetch server policy over the network when the cache is stale, so call it off latency-critical paths such as startup rather than before listing models. The policy helper is not run. `layers` lists each channel's document before merging, and `values` and `meta` carry typed effective values and their lock state for the keys typed so far. /// - /// * `params` - Read-only preparation of one owned removal under fresh selected-session authority. + /// Wire method: `managedSettings.resolve`. /// /// # Returns /// - /// Management result with contract receipt, or a typed request/negotiation refusal. + /// Effective enterprise managed settings for an account, resolved without a session. /// ///
/// @@ -2378,29 +2872,26 @@ impl<'a> ClientRpcMcp<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn plan_uninstall( - &self, - params: McpPlanUninstallRequest, - ) -> Result { - let wire_params = serde_json::to_value(params)?; + pub async fn resolve(&self) -> Result { + let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::MCP_PLANUNINSTALL, Some(wire_params)) + .call(rpc_methods::MANAGEDSETTINGS_RESOLVE, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Consumes the original owned-removal plan once and requests fresh exact human confirmation on its original connection. Drift is refused; unrelated manual configuration and shared OAuth credentials are preserved. + /// Resolves the effective enterprise managed settings without a session, from the device channel and, when an account is available, the account's server policy through the same per-account cache sessions use. A cached server policy less than an hour old is used without a fetch; otherwise the policy is fetched, and when the fetch fails a cached policy up to 24 hours old is used instead, unless `forceRemoteSettingsRefresh` requires a live fetch. With no account requested or signed in, it reports device policy only; signing out removes the account's cached policy. It can fetch server policy over the network when the cache is stale, so call it off latency-critical paths such as startup rather than before listing models. The policy helper is not run. `layers` lists each channel's document before merging, and `values` and `meta` carry typed effective values and their lock state for the keys typed so far. /// - /// Wire method: `mcp.applyUninstall`. + /// Wire method: `managedSettings.resolve`. /// /// # Parameters /// - /// * `params` - One-use application of the exact retained removal plan. + /// * `params` - Optional opaque account selection or GitHub token whose managed settings are resolved. /// /// # Returns /// - /// An installation result together with the exact honoured contract, or a negotiation refusal. + /// Effective enterprise managed settings for an account, resolved without a session. /// ///
/// @@ -2409,33 +2900,25 @@ impl<'a> ClientRpcMcp<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn apply_uninstall( + pub async fn resolve_with_params( &self, - params: McpApplyUninstallRequest, - ) -> Result { + params: ManagedSettingsResolveRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MCP_APPLYUNINSTALL, Some(wire_params)) + .call(rpc_methods::MANAGEDSETTINGS_RESOLVE, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } -} - -/// `mcp.config.*` RPCs. -#[derive(Clone, Copy)] -pub struct ClientRpcMcpConfig<'a> { - pub(crate) client: &'a Client, -} -impl<'a> ClientRpcMcpConfig<'a> { - /// Lists MCP servers from user configuration. + /// Returns the managed-settings authoring JSON schema with descriptive `x-composition` annotations aligned with the shared settings-engine vocabulary. These annotations are not a complete runtime composition contract: model, effortLevel, and contextTier remain coupled. Use `managedSettings.compose` for the runtime's effective result. Performs no I/O. /// - /// Wire method: `mcp.config.list`. + /// Wire method: `managedSettings.schema`. /// /// # Returns /// - /// User-configured MCP servers, keyed by server name. + /// The authoring JSON schema for managed settings recognized by this runtime. /// ///
/// @@ -2444,22 +2927,26 @@ impl<'a> ClientRpcMcpConfig<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn list(&self) -> Result { + pub async fn schema(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::MCP_CONFIG_LIST, Some(wire_params)) + .call(rpc_methods::MANAGEDSETTINGS_SCHEMA, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Adds an MCP server to user configuration. + /// Validates a candidate managed-settings document the way the runtime validates delivered policy, without applying it. Reports errors that would reject the document, warnings for content the runtime ignores, and the canonical document it would apply. Document text nested more than 64 levels deep is rejected. Performs no I/O. /// - /// Wire method: `mcp.config.add`. + /// Wire method: `managedSettings.validate`. /// /// # Parameters /// - /// * `params` - MCP server name and configuration to add to user configuration. + /// * `params` - A candidate managed-settings document to validate without applying it. + /// + /// # Returns + /// + /// Result of validating a managed-settings document. /// ///
/// @@ -2468,22 +2955,29 @@ impl<'a> ClientRpcMcpConfig<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn add(&self, params: McpConfigAddRequest) -> Result<(), Error> { + pub async fn validate( + &self, + params: ManagedSettingsValidateRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MCP_CONFIG_ADD, Some(wire_params)) + .call(rpc_methods::MANAGEDSETTINGS_VALIDATE, Some(wire_params)) .await?; - Ok(()) + Ok(serde_json::from_value(_value)?) } - /// Updates an MCP server in user configuration. + /// Merges candidate managed-settings documents for the device, server, and policy-helper channels into the effective settings the runtime would enforce on this host, using the same precedence and composition rules as live resolution, without applying them. Like live resolution, a server's advisory sandbox force-enable is declined on a host that cannot run the sandbox. Does not fetch policy or read policy files, but may perform blocking OS or subprocess probes for sandbox support. Preview documents are limited to 1 MiB and 64 levels of nesting. /// - /// Wire method: `mcp.config.update`. + /// Wire method: `managedSettings.compose`. /// /// # Parameters /// - /// * `params` - MCP server name and replacement configuration to write to user configuration. + /// * `params` - Candidate managed-settings documents to merge without applying them. + /// + /// # Returns + /// + /// The effective managed settings the runtime would enforce for the given documents, in the same shape `managedSettings.resolve` returns. /// ///
/// @@ -2492,22 +2986,37 @@ impl<'a> ClientRpcMcpConfig<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn update(&self, params: McpConfigUpdateRequest) -> Result<(), Error> { + pub async fn compose( + &self, + params: ManagedSettingsComposeRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MCP_CONFIG_UPDATE, Some(wire_params)) + .call(rpc_methods::MANAGEDSETTINGS_COMPOSE, Some(wire_params)) .await?; - Ok(()) + Ok(serde_json::from_value(_value)?) } +} - /// Removes an MCP server from user configuration. +/// `managedSettings.permissions.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcManagedSettingsPermissions<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcManagedSettingsPermissions<'a> { + /// Pure, sessionless evaluation of operations against a supplied permissionsContext. Performs no policy discovery, I/O, prompting, navigation, permission grants, or sandbox changes. Obtain the context from managedSettings.resolve or session.managedSettings.get, or reuse an in-memory context. Results preserve input order and duplicates. Consumers must block deny, obtain approval for ask, and use their normal permission flow for unmanaged. Unknown policy returns failClosed with every operation denied. This is not a substitute for user permissions or other security controls. /// - /// Wire method: `mcp.config.remove`. + /// Wire method: `managedSettings.permissions.evaluate`. /// /// # Parameters /// - /// * `params` - MCP server name to remove from user configuration. + /// * `params` - Managed policy context and ordered operations for pure, sessionless evaluation. + /// + /// # Returns + /// + /// Ordered managed permission verdicts and the supplied policy's fail-closed posture. /// ///
/// @@ -2516,22 +3025,61 @@ impl<'a> ClientRpcMcpConfig<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn remove(&self, params: McpConfigRemoveRequest) -> Result<(), Error> { + pub async fn evaluate( + &self, + params: ManagedPermissionsEvaluateRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MCP_CONFIG_REMOVE, Some(wire_params)) + .call( + rpc_methods::MANAGEDSETTINGS_PERMISSIONS_EVALUATE, + Some(wire_params), + ) .await?; - Ok(()) + Ok(serde_json::from_value(_value)?) } +} - /// Enables MCP servers in user configuration for new sessions. +/// `mcp.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcMcp<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcMcp<'a> { + /// `mcp.config.*` sub-namespace. + pub fn config(&self) -> ClientRpcMcpConfig<'a> { + ClientRpcMcpConfig { + client: self.client, + } + } + + /// `mcp.installations.*` sub-namespace. + pub fn installations(&self) -> ClientRpcMcpInstallations<'a> { + ClientRpcMcpInstallations { + client: self.client, + } + } + + /// `mcp.registry.*` sub-namespace. + pub fn registry(&self) -> ClientRpcMcpRegistry<'a> { + ClientRpcMcpRegistry { + client: self.client, + } + } + + /// Discovers MCP servers from user, workspace, plugin, and builtin sources. /// - /// Wire method: `mcp.config.enable`. + /// Wire method: `mcp.discover`. /// /// # Parameters /// - /// * `params` - MCP server names to enable for new sessions. + /// * `params` - Optional working directory used as context for MCP server discovery. + /// + /// # Returns + /// + /// MCP servers discovered from user, workspace, plugin, and built-in sources. /// ///
/// @@ -2540,22 +3088,22 @@ impl<'a> ClientRpcMcpConfig<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn enable(&self, params: McpConfigEnableRequest) -> Result<(), Error> { + pub async fn discover(&self, params: McpDiscoverRequest) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MCP_CONFIG_ENABLE, Some(wire_params)) + .call(rpc_methods::MCP_DISCOVER, Some(wire_params)) .await?; - Ok(()) + Ok(serde_json::from_value(_value)?) } - /// Disables MCP servers in user configuration for new sessions. + /// Lists the MCP servers the runtime supplies itself, so a host that disables the built-ins wholesale suppresses exactly the set the runtime would otherwise add. The names are fixed for a build and depend on no configuration, session or account. /// - /// Wire method: `mcp.config.disable`. + /// Wire method: `mcp.listBuiltinServerNames`. /// - /// # Parameters + /// # Returns /// - /// * `params` - MCP server names to disable for new sessions. + /// Names of the MCP servers the runtime supplies itself. /// ///
/// @@ -2564,54 +3112,28 @@ impl<'a> ClientRpcMcpConfig<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn disable(&self, params: McpConfigDisableRequest) -> Result<(), Error> { - let wire_params = serde_json::to_value(params)?; + pub(crate) async fn list_builtin_server_names( + &self, + ) -> Result { + let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::MCP_CONFIG_DISABLE, Some(wire_params)) + .call(rpc_methods::MCP_LISTBUILTINSERVERNAMES, Some(wire_params)) .await?; - Ok(()) + Ok(serde_json::from_value(_value)?) } - /// Drops this runtime process's in-memory MCP server-definition cache so the next MCP config read observes disk. + /// Reports whether the gh-replaceable GitHub MCP tools may be clipped for a session, which a host feeds back as the `excludeGhReplaceableTools` build option. The tools are redundant only when the session can reach a shell *and* the host has the `gh` that would replace them, so both halves are decided here. The shell half runs the platform shell tool through the runtime's own tool-filter matcher, so entry forms like `builtin:bash` and `builtin:*` behave exactly as they do when a session builds its tool catalog. The host half probes for `gh`, and is skipped entirely when the filters already rule the shell out. Host presence alone is not enough: a session restricted to, say, `view` would otherwise lose the built-in issue-read fallback while having no shell to replace it with. The answer describes the host the runtime runs on, so it is never forwarded to a remote engine. /// - /// Wire method: `mcp.config.reload`. + /// Wire method: `mcp.shouldExcludeGitHubTools`. /// - ///
+ /// # Parameters /// - /// **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. - /// - ///
- pub async fn reload(&self) -> Result<(), Error> { - let wire_params = serde_json::json!({}); - let _value = self - .client - .call(rpc_methods::MCP_CONFIG_RELOAD, Some(wire_params)) - .await?; - Ok(()) - } -} - -/// `mcp.installations.*` RPCs. -#[derive(Clone, Copy)] -pub struct ClientRpcMcpInstallations<'a> { - pub(crate) client: &'a Client, -} - -impl<'a> ClientRpcMcpInstallations<'a> { - /// Reads receipt-owned MCP inventory for the selected account and host without activating servers or reconstructing missing ownership. Configuration ownership does not prove session-specific usability. - /// - /// Wire method: `mcp.installations.list`. - /// - /// # Parameters - /// - /// * `params` - New-work inventory or recovery request under an explicitly selected existing session. + /// * `params` - The session tool filters that decide whether the session still has a shell to run `gh` with. /// /// # Returns /// - /// Management result with contract receipt, or a typed request/negotiation refusal. + /// Whether the gh-replaceable GitHub MCP tools may be clipped for the session described by the request. /// ///
/// @@ -2620,29 +3142,29 @@ impl<'a> ClientRpcMcpInstallations<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn list( + pub(crate) async fn should_exclude_git_hub_tools( &self, - params: McpInstallationsRequest, - ) -> Result { + params: McpShouldExcludeGitHubToolsRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MCP_INSTALLATIONS_LIST, Some(wire_params)) + .call(rpc_methods::MCP_SHOULDEXCLUDEGITHUBTOOLS, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Reconciles already-confirmed durable MCP transactions, then inspects owned inventory. Does not replay apply or reconstruct deleted ownership metadata; unresolved or unsafe evidence remains an explicit refusal. + /// Requests a side-effect-free MCP install plan from a catalog candidate handle or a caller-supplied card. This host-implemented server method is available through SDK/TUI hosts; standalone and C-ABI runtimes whose host does not implement server-method dispatch return JSON-RPC MethodNotFound. A runtime with planning available returns a normalised plan and opaque single-use plan handle; a runtime without it returns the typed planning-unavailable result. A completed plan reports resource identity, provenance, eligible transport choices, the user-scope target, required typed values and secret placeholders, the policy result, the configuration changes installing would make, and whether a reload would be needed. Planning never writes configuration, stores a secret, or reloads MCP servers, so abandoning a plan needs no call and leaves nothing behind. /// - /// Wire method: `mcp.installations.recover`. + /// Wire method: `mcp.planInstall`. /// /// # Parameters /// - /// * `params` - New-work inventory or recovery request under an explicitly selected existing session. + /// * `params` - A side-effect-free request for an MCP install plan. Computing a plan never writes configuration, stores a secret, or reloads MCP servers. /// /// # Returns /// - /// Management result with contract receipt, or a typed request/negotiation refusal. + /// Outcome of an mcp.planInstall call: either a normalised plan, or one typed refusal. Nothing is written in either case. /// ///
/// @@ -2651,29 +3173,29 @@ impl<'a> ClientRpcMcpInstallations<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn recover( + pub async fn plan_install( &self, - params: McpInstallationsRequest, - ) -> Result { + params: McpPlanInstallRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MCP_INSTALLATIONS_RECOVER, Some(wire_params)) + .call(rpc_methods::MCP_PLANINSTALL, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Inspects a known operation only on its original connection. Remains available after account or selected-session loss; does not acquire new authority or rebind an operation. + /// Requests a side-effect-free MCP install plan from a catalog candidate handle or a caller-supplied card. This host-implemented server method is available through SDK/TUI hosts; standalone and C-ABI runtimes whose host does not implement server-method dispatch return JSON-RPC MethodNotFound. A runtime with planning available returns a normalised plan and opaque single-use plan handle; a runtime without it returns the typed planning-unavailable result. A completed plan reports resource identity, provenance, eligible transport choices, the user-scope target, required typed values and secret placeholders, the policy result, the configuration changes installing would make, and whether a reload would be needed. Planning never writes configuration, stores a secret, or reloads MCP servers, so abandoning a plan needs no call and leaves nothing behind. /// - /// Wire method: `mcp.installations.status`. + /// Wire method: `mcp.planInstall`. /// /// # Parameters /// - /// * `params` - Existing-operation control. A new session selector is deliberately not accepted. + /// * `params` - A side-effect-free request for an MCP install plan. Computing a plan never writes configuration, stores a secret, or reloads MCP servers. /// /// # Returns /// - /// Management result with contract receipt, or a typed request/negotiation refusal. + /// Outcome of an mcp.planInstall call: either a normalised plan, or one typed refusal. Nothing is written in either case. /// ///
/// @@ -2682,25 +3204,27 @@ impl<'a> ClientRpcMcpInstallations<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn status( + /// + /// Accepts [`McpPlanInstallOptions`], including inputs added after [`McpPlanInstallRequest`]. + pub async fn plan_install_with_options( &self, - params: McpInstallationOperationRequest, - ) -> Result { + params: McpPlanInstallOptions, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MCP_INSTALLATIONS_STATUS, Some(wire_params)) + .call(rpc_methods::MCP_PLANINSTALL, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Requests cancellation of a known operation on its original connection, including before apply or confirmation. Already-started effects retain their transaction lease and report an honest terminal or recovery outcome. + /// Consumes a bound catalogue plan and retains one exact fully resolved personal remote MCP operation requiring no supplied values or configured secrets. Returns its runtime operation ID and original expiry before any confirmation, activation, writer initialisation or installation effect. Register the original connection, operation and selected-session binding before calling applyInstall. Missing lower owned admission is unavailable, never a raw-config fallback. /// - /// Wire method: `mcp.installations.cancel`. + /// Wire method: `mcp.prepareInstall`. /// /// # Parameters /// - /// * `params` - Existing-operation control. A new session selector is deliberately not accepted. + /// * `params` - Side-effect-free preparation of one original bound remote MCP choice. /// /// # Returns /// @@ -2713,33 +3237,29 @@ impl<'a> ClientRpcMcpInstallations<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn cancel( + pub async fn prepare_install( &self, - params: McpInstallationOperationRequest, + params: McpPrepareInstallRequest, ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MCP_INSTALLATIONS_CANCEL, Some(wire_params)) + .call(rpc_methods::MCP_PREPAREINSTALL, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } -} - -/// `models.*` RPCs. -#[derive(Clone, Copy)] -pub struct ClientRpcModels<'a> { - pub(crate) client: &'a Client, -} -impl<'a> ClientRpcModels<'a> { - /// Lists Copilot models available to the authenticated user. + /// Consumes a retained prepared MCP operation once, revalidates its original authority, requests explicit human consent through installations.confirm on the original connection, then revalidates source and applies the sealed transaction. An uncertain result requires original-operation inspection or recovery, never replay. /// - /// Wire method: `models.list`. + /// Wire method: `mcp.applyInstall`. + /// + /// # Parameters + /// + /// * `params` - Applies exactly one previously prepared operation on its original connection. /// /// # Returns /// - /// List of Copilot models available to the resolved user, including capabilities and billing metadata. + /// An installation result together with the exact honoured contract, or a negotiation refusal. /// ///
/// @@ -2748,26 +3268,29 @@ impl<'a> ClientRpcModels<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn list(&self) -> Result { - let wire_params = serde_json::json!({}); + pub async fn apply_install( + &self, + params: McpApplyInstallRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MODELS_LIST, Some(wire_params)) + .call(rpc_methods::MCP_APPLYINSTALL, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Lists Copilot models available to the authenticated user. + /// Prepares a read-only removal plan for an exact owned receipt under the selected existing session. Returns the original operation ID before confirmation; neither planning nor abandonment changes configuration or shared OAuth credentials. /// - /// Wire method: `models.list`. + /// Wire method: `mcp.planUninstall`. /// /// # Parameters /// - /// * `params` - Optional opaque account selection or compatibility GitHub token used to list models. + /// * `params` - Read-only preparation of one owned removal under fresh selected-session authority. /// /// # Returns /// - /// List of Copilot models available to the resolved user, including capabilities and billing metadata. + /// Management result with contract receipt, or a typed request/negotiation refusal. /// ///
/// @@ -2776,22 +3299,29 @@ impl<'a> ClientRpcModels<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn list_with_params(&self, params: ModelsListRequest) -> Result { + pub async fn plan_uninstall( + &self, + params: McpPlanUninstallRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MODELS_LIST, Some(wire_params)) + .call(rpc_methods::MCP_PLANUNINSTALL, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Returns the running runtime's complete catalog of well-known built-in model IDs without authentication or network access. + /// Consumes the original owned-removal plan once and requests fresh exact human confirmation on its original connection. Drift is refused; unrelated manual configuration and shared OAuth credentials are preserved. /// - /// Wire method: `models.getBuiltInCatalog`. + /// Wire method: `mcp.applyUninstall`. + /// + /// # Parameters + /// + /// * `params` - One-use application of the exact retained removal plan. /// /// # Returns /// - /// The running runtime's complete catalog of well-known built-in model IDs, including supported models and additional IDs with built-in metadata. + /// An installation result together with the exact honoured contract, or a negotiation refusal. /// ///
/// @@ -2800,44 +3330,33 @@ impl<'a> ClientRpcModels<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn get_built_in_catalog(&self) -> Result { - let wire_params = serde_json::json!({}); + pub async fn apply_uninstall( + &self, + params: McpApplyUninstallRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MODELS_GETBUILTINCATALOG, Some(wire_params)) + .call(rpc_methods::MCP_APPLYUNINSTALL, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } } -/// `plugins.*` RPCs. +/// `mcp.config.*` RPCs. #[derive(Clone, Copy)] -pub struct ClientRpcPlugins<'a> { +pub struct ClientRpcMcpConfig<'a> { pub(crate) client: &'a Client, } -impl<'a> ClientRpcPlugins<'a> { - /// `plugins.builtin.*` sub-namespace. - pub fn builtin(&self) -> ClientRpcPluginsBuiltin<'a> { - ClientRpcPluginsBuiltin { - client: self.client, - } - } - - /// `plugins.marketplaces.*` sub-namespace. - pub fn marketplaces(&self) -> ClientRpcPluginsMarketplaces<'a> { - ClientRpcPluginsMarketplaces { - client: self.client, - } - } - - /// Lists plugins installed in user/global state. +impl<'a> ClientRpcMcpConfig<'a> { + /// Lists MCP servers from user configuration. /// - /// Wire method: `plugins.list`. + /// Wire method: `mcp.config.list`. /// /// # Returns /// - /// Plugins installed in user/global state. + /// User-configured MCP servers, keyed by server name. /// ///
/// @@ -2846,26 +3365,22 @@ impl<'a> ClientRpcPlugins<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn list(&self) -> Result { + pub async fn list(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::PLUGINS_LIST, Some(wire_params)) + .call(rpc_methods::MCP_CONFIG_LIST, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Installs a plugin from a marketplace, GitHub repo, URL, or local path. + /// Adds an MCP server to user configuration. /// - /// Wire method: `plugins.install`. + /// Wire method: `mcp.config.add`. /// /// # Parameters /// - /// * `params` - Plugin source and optional working directory for relative-path resolution. - /// - /// # Returns - /// - /// Result of installing a plugin. + /// * `params` - MCP server name and configuration to add to user configuration. /// ///
/// @@ -2874,25 +3389,22 @@ impl<'a> ClientRpcPlugins<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn install( - &self, - params: PluginsInstallRequest, - ) -> Result { + pub async fn add(&self, params: McpConfigAddRequest) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::PLUGINS_INSTALL, Some(wire_params)) + .call(rpc_methods::MCP_CONFIG_ADD, Some(wire_params)) .await?; - Ok(serde_json::from_value(_value)?) + Ok(()) } - /// Uninstalls an installed plugin. + /// Updates an MCP server in user configuration. /// - /// Wire method: `plugins.uninstall`. + /// Wire method: `mcp.config.update`. /// /// # Parameters /// - /// * `params` - Name (or spec) of the plugin to uninstall. + /// * `params` - MCP server name and replacement configuration to write to user configuration. /// ///
/// @@ -2901,26 +3413,22 @@ impl<'a> ClientRpcPlugins<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn uninstall(&self, params: PluginsUninstallRequest) -> Result<(), Error> { + pub async fn update(&self, params: McpConfigUpdateRequest) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::PLUGINS_UNINSTALL, Some(wire_params)) + .call(rpc_methods::MCP_CONFIG_UPDATE, Some(wire_params)) .await?; Ok(()) } - /// Updates an installed plugin to its latest published version. + /// Removes an MCP server from user configuration. /// - /// Wire method: `plugins.update`. + /// Wire method: `mcp.config.remove`. /// /// # Parameters /// - /// * `params` - Name (or spec) of the plugin to update. - /// - /// # Returns - /// - /// Result of updating a single plugin. + /// * `params` - MCP server name to remove from user configuration. /// ///
/// @@ -2929,22 +3437,22 @@ impl<'a> ClientRpcPlugins<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn update(&self, params: PluginsUpdateRequest) -> Result { + pub async fn remove(&self, params: McpConfigRemoveRequest) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::PLUGINS_UPDATE, Some(wire_params)) + .call(rpc_methods::MCP_CONFIG_REMOVE, Some(wire_params)) .await?; - Ok(serde_json::from_value(_value)?) + Ok(()) } - /// Updates every installed plugin to its latest published version. + /// Enables MCP servers in user configuration for new sessions. /// - /// Wire method: `plugins.updateAll`. + /// Wire method: `mcp.config.enable`. /// - /// # Returns + /// # Parameters /// - /// Result of updating all installed plugins. + /// * `params` - MCP server names to enable for new sessions. /// ///
/// @@ -2953,22 +3461,22 @@ impl<'a> ClientRpcPlugins<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn update_all(&self) -> Result { - let wire_params = serde_json::json!({}); + pub async fn enable(&self, params: McpConfigEnableRequest) -> Result<(), Error> { + let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::PLUGINS_UPDATEALL, Some(wire_params)) + .call(rpc_methods::MCP_CONFIG_ENABLE, Some(wire_params)) .await?; - Ok(serde_json::from_value(_value)?) + Ok(()) } - /// Enables installed plugins for new sessions. + /// Disables MCP servers in user configuration for new sessions. /// - /// Wire method: `plugins.enable`. + /// Wire method: `mcp.config.disable`. /// /// # Parameters /// - /// * `params` - Plugin names (or specs) to enable, plus the optional working directory the repository-controlled guard is evaluated against. + /// * `params` - MCP server names to disable for new sessions. /// ///
/// @@ -2977,22 +3485,18 @@ impl<'a> ClientRpcPlugins<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn enable(&self, params: PluginsEnableRequest) -> Result<(), Error> { + pub async fn disable(&self, params: McpConfigDisableRequest) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::PLUGINS_ENABLE, Some(wire_params)) + .call(rpc_methods::MCP_CONFIG_DISABLE, Some(wire_params)) .await?; Ok(()) } - /// Disables installed plugins for new sessions. - /// - /// Wire method: `plugins.disable`. - /// - /// # Parameters + /// Drops this runtime process's in-memory MCP server-definition cache so the next MCP config read observes disk. /// - /// * `params` - Plugin names (or specs) to disable, plus the optional working directory the repository-controlled guard is evaluated against. + /// Wire method: `mcp.config.reload`. /// ///
/// @@ -3001,62 +3505,34 @@ impl<'a> ClientRpcPlugins<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn disable(&self, params: PluginsDisableRequest) -> Result<(), Error> { - let wire_params = serde_json::to_value(params)?; + pub async fn reload(&self) -> Result<(), Error> { + let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::PLUGINS_DISABLE, Some(wire_params)) + .call(rpc_methods::MCP_CONFIG_RELOAD, Some(wire_params)) .await?; Ok(()) } } -/// `plugins.builtin.*` RPCs. +/// `mcp.installations.*` RPCs. #[derive(Clone, Copy)] -pub struct ClientRpcPluginsBuiltin<'a> { +pub struct ClientRpcMcpInstallations<'a> { pub(crate) client: &'a Client, } -impl<'a> ClientRpcPluginsBuiltin<'a> { - /// Replaces this server's trusted built-in plugin directories while no sessions are active. +impl<'a> ClientRpcMcpInstallations<'a> { + /// Reads receipt-owned MCP inventory for the selected account and host without activating servers or reconstructing missing ownership. Configuration ownership does not prove session-specific usability. /// - /// Wire method: `plugins.builtin.set`. + /// Wire method: `mcp.installations.list`. /// /// # Parameters /// - /// * `params` - Trusted built-in plugin directories to use for this runtime process. - /// - ///
- /// - /// **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. - /// - ///
- pub async fn set(&self, params: PluginsBuiltinSetRequest) -> Result<(), Error> { - let wire_params = serde_json::to_value(params)?; - let _value = self - .client - .call(rpc_methods::PLUGINS_BUILTIN_SET, Some(wire_params)) - .await?; - Ok(()) - } -} - -/// `plugins.marketplaces.*` RPCs. -#[derive(Clone, Copy)] -pub struct ClientRpcPluginsMarketplaces<'a> { - pub(crate) client: &'a Client, -} - -impl<'a> ClientRpcPluginsMarketplaces<'a> { - /// Lists all registered marketplaces (defaults + user-added). - /// - /// Wire method: `plugins.marketplaces.list`. + /// * `params` - New-work inventory or recovery request under an explicitly selected existing session. /// /// # Returns /// - /// All registered marketplaces, including built-in defaults. + /// Management result with contract receipt, or a typed request/negotiation refusal. /// ///
/// @@ -3065,26 +3541,29 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn list(&self) -> Result { - let wire_params = serde_json::json!({}); + pub async fn list( + &self, + params: McpInstallationsRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::PLUGINS_MARKETPLACES_LIST, Some(wire_params)) + .call(rpc_methods::MCP_INSTALLATIONS_LIST, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Registers a new marketplace from a source (owner/repo, URL, or local path). + /// Reconciles already-confirmed durable MCP transactions, then inspects owned inventory. Does not replay apply or reconstruct deleted ownership metadata; unresolved or unsafe evidence remains an explicit refusal. /// - /// Wire method: `plugins.marketplaces.add`. + /// Wire method: `mcp.installations.recover`. /// /// # Parameters /// - /// * `params` - Marketplace source and optional working directory for relative-path resolution. + /// * `params` - New-work inventory or recovery request under an explicitly selected existing session. /// /// # Returns /// - /// Result of registering a new marketplace. + /// Management result with contract receipt, or a typed request/negotiation refusal. /// ///
/// @@ -3093,29 +3572,29 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn add( + pub async fn recover( &self, - params: PluginsMarketplacesAddRequest, - ) -> Result { + params: McpInstallationsRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::PLUGINS_MARKETPLACES_ADD, Some(wire_params)) + .call(rpc_methods::MCP_INSTALLATIONS_RECOVER, 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`. + /// Inspects a known operation only on its original connection. Remains available after account or selected-session loss; does not acquire new authority or rebind an operation. /// - /// Wire method: `plugins.marketplaces.remove`. + /// Wire method: `mcp.installations.status`. /// /// # Parameters /// - /// * `params` - Name of the marketplace to remove and an optional force flag. + /// * `params` - Existing-operation control. A new session selector is deliberately not accepted. /// /// # Returns /// - /// Outcome of the remove attempt, including dependent-plugin info when applicable. + /// Management result with contract receipt, or a typed request/negotiation refusal. /// ///
/// @@ -3124,29 +3603,29 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn remove( + pub async fn status( &self, - params: PluginsMarketplacesRemoveRequest, - ) -> Result { + params: McpInstallationOperationRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::PLUGINS_MARKETPLACES_REMOVE, Some(wire_params)) + .call(rpc_methods::MCP_INSTALLATIONS_STATUS, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Lists plugins advertised by a registered marketplace. + /// Requests cancellation of a known operation on its original connection, including before apply or confirmation. Already-started effects retain their transaction lease and report an honest terminal or recovery outcome. /// - /// Wire method: `plugins.marketplaces.browse`. + /// Wire method: `mcp.installations.cancel`. /// /// # Parameters /// - /// * `params` - Name of the marketplace whose plugin catalog to fetch. + /// * `params` - Existing-operation control. A new session selector is deliberately not accepted. /// /// # Returns /// - /// Plugins advertised by the marketplace. + /// Management result with contract receipt, or a typed request/negotiation refusal. /// ///
/// @@ -3155,25 +3634,33 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn browse( + pub async fn cancel( &self, - params: PluginsMarketplacesBrowseRequest, - ) -> Result { + params: McpInstallationOperationRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::PLUGINS_MARKETPLACES_BROWSE, Some(wire_params)) + .call(rpc_methods::MCP_INSTALLATIONS_CANCEL, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } +} - /// Re-fetches one or all registered marketplace catalogs. +/// `mcp.registry.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcMcpRegistry<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcMcpRegistry<'a> { + /// Allocates an ID for one cancellable MCP registry search. The ID exists before the search starts, so callers can cancel before it starts. The networking stack supplies the cancellation namespace. The runtime retains at most 1,024 unused IDs. At capacity, another allocation can reclaim an unused ID. Active searches retain their IDs until they finish. /// - /// Wire method: `plugins.marketplaces.refresh`. + /// Wire method: `mcp.registry.allocateRequestId`. /// /// # Returns /// - /// Result of refreshing one or more marketplace catalogs. + /// Request id naming a cancellable registry search. /// ///
/// @@ -3182,26 +3669,29 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn refresh(&self) -> Result { + pub(crate) async fn allocate_request_id(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::PLUGINS_MARKETPLACES_REFRESH, Some(wire_params)) + .call( + rpc_methods::MCP_REGISTRY_ALLOCATEREQUESTID, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } - /// Re-fetches one or all registered marketplace catalogs. + /// Searches the MCP registry the supplied credential may read, resolving the registry endpoint from policy first. Hosts usually send credential-free `AuthIdentity`; a `token` identity can be resolved only when it embeds a token, while `env` and `gh-cli` identities can use a token embedded in the request first. The runtime follows registry pages, keeps the newest entry per server name, cuts the list to `limit`, and sorts an empty-query result by GitHub stars. Each server object is carried opaquely. /// - /// Wire method: `plugins.marketplaces.refresh`. + /// Wire method: `mcp.registry.search`. /// /// # Parameters /// - /// * `params` - Optional marketplace name; omit to refresh all. + /// * `params` - Registry search terms, the credential to search under, and the request id that makes the search cancellable. /// /// # Returns /// - /// Result of refreshing one or more marketplace catalogs. + /// Servers selected from the registry response. /// ///
/// @@ -3210,29 +3700,29 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn refresh_with_params( + pub(crate) async fn search( &self, - params: PluginsMarketplacesRefreshRequest, - ) -> Result { + params: McpRegistrySearchRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::PLUGINS_MARKETPLACES_REFRESH, Some(wire_params)) + .call(rpc_methods::MCP_REGISTRY_SEARCH, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } -} - -/// `runtime.*` RPCs. -#[derive(Clone, Copy)] -pub struct ClientRpcRuntime<'a> { - pub(crate) client: &'a Client, -} -impl<'a> ClientRpcRuntime<'a> { - /// Gracefully shuts down an SDK-owned runtime. The response is sent only after cleanup completes; callers may then terminate the owned runtime process. + /// Abandons the registry search that uses the given request ID. It acts only on IDs from `mcp.registry.allocateRequestId`, so it never cancels another component's request. Answers `canceled: true` when it stops a running search. Answers `canceled: false` for unknown or reclaimed IDs, completed or canceled searches, and unused reservations. It releases an unused reservation, so a later search with that ID is refused. /// - /// Wire method: `runtime.shutdown`. + /// Wire method: `mcp.registry.cancel`. + /// + /// # Parameters + /// + /// * `params` - Registry search to abandon. + /// + /// # Returns + /// + /// Whether the cancel reached a live search. /// ///
/// @@ -3241,37 +3731,33 @@ impl<'a> ClientRpcRuntime<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn shutdown(&self) -> Result<(), Error> { - let wire_params = serde_json::json!({}); + pub(crate) async fn cancel( + &self, + params: McpRegistryCancelRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::RUNTIME_SHUTDOWN, Some(wire_params)) + .call(rpc_methods::MCP_REGISTRY_CANCEL, Some(wire_params)) .await?; - Ok(()) + Ok(serde_json::from_value(_value)?) } } -/// `sandbox.*` RPCs. +/// `models.*` RPCs. #[derive(Clone, Copy)] -pub struct ClientRpcSandbox<'a> { +pub struct ClientRpcModels<'a> { pub(crate) client: &'a Client, } -impl<'a> ClientRpcSandbox<'a> { - /// `sandbox.proxyCa.*` sub-namespace. - pub fn proxy_ca(&self) -> ClientRpcSandboxProxyCa<'a> { - ClientRpcSandboxProxyCa { - client: self.client, - } - } - - /// Reports whether the host running this runtime can run the command sandbox, without starting a session or spawning a sandboxed command. +impl<'a> ClientRpcModels<'a> { + /// Lists Copilot models available to the authenticated user. /// - /// Wire method: `sandbox.getHostSupport`. + /// Wire method: `models.list`. /// /// # Returns /// - /// Whether the host running this runtime can run the command sandbox. The runtime checks `supported` once per process. A capability answer can change while the process runs, for example after the user installs a missing package. + /// List of Copilot models available to the resolved user, including capabilities and billing metadata. /// ///
/// @@ -3280,34 +3766,26 @@ impl<'a> ClientRpcSandbox<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn get_host_support(&self) -> Result { + pub async fn list(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::SANDBOX_GETHOSTSUPPORT, Some(wire_params)) + .call(rpc_methods::MODELS_LIST, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } -} - -/// `sandbox.proxyCa.*` RPCs. -#[derive(Clone, Copy)] -pub struct ClientRpcSandboxProxyCa<'a> { - pub(crate) client: &'a Client, -} -impl<'a> ClientRpcSandboxProxyCa<'a> { - /// Reports whether the persistent certificate authority of the sandbox credential proxy exists, whether OS trust includes it, and whether it must be rotated. Changes nothing. + /// Lists Copilot models available to the authenticated user. /// - /// Wire method: `sandbox.proxyCa.getStatus`. + /// Wire method: `models.list`. /// /// # Parameters /// - /// * `params` - Identifies the credential hosts that the persistent certificate authority of the sandbox credential proxy must cover. The runtime always adds the hosts from the saved user settings. + /// * `params` - Optional opaque account selection or compatibility GitHub token used to list models. /// /// # Returns /// - /// Status of the persistent certificate authority of the sandbox credential proxy. + /// List of Copilot models available to the resolved user, including capabilities and billing metadata. /// ///
/// @@ -3316,29 +3794,22 @@ impl<'a> ClientRpcSandboxProxyCa<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn get_status( - &self, - params: SandboxProxyCaRequest, - ) -> Result { + pub async fn list_with_params(&self, params: ModelsListRequest) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SANDBOX_PROXYCA_GETSTATUS, Some(wire_params)) + .call(rpc_methods::MODELS_LIST, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Creates the persistent certificate authority of the sandbox credential proxy if none is stored, without changing OS trust, and returns the path of its public certificate. Keeps an existing certificate authority, even one that must be rotated. Fails where OS trust is unsupported. Trust it with sandbox.proxyCa.trust: the CLI trusts only the hosts in the saved user settings, so it refuses a certificate authority that also covers hosts from sandboxConfig. - /// - /// Wire method: `sandbox.proxyCa.create`. - /// - /// # Parameters + /// Returns the running runtime's complete catalog of well-known built-in model IDs without authentication or network access. /// - /// * `params` - Identifies the credential hosts that the persistent certificate authority of the sandbox credential proxy must cover. The runtime always adds the hosts from the saved user settings. + /// Wire method: `models.getBuiltInCatalog`. /// /// # Returns /// - /// Result of creating the persistent certificate authority of the sandbox credential proxy. + /// The running runtime's complete catalog of well-known built-in model IDs, including supported models and additional IDs with built-in metadata. /// ///
/// @@ -3347,29 +3818,48 @@ impl<'a> ClientRpcSandboxProxyCa<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn create( - &self, - params: SandboxProxyCaRequest, - ) -> Result { - let wire_params = serde_json::to_value(params)?; + pub async fn get_built_in_catalog(&self) -> Result { + let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::SANDBOX_PROXYCA_CREATE, Some(wire_params)) + .call(rpc_methods::MODELS_GETBUILTINCATALOG, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } +} + +/// `permissions.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcPermissions<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcPermissions<'a> { + /// `permissions.folderTrust.*` sub-namespace. + pub fn folder_trust(&self) -> ClientRpcPermissionsFolderTrust<'a> { + ClientRpcPermissionsFolderTrust { + client: self.client, + } + } - /// Replaces the persistent certificate authority of the sandbox credential proxy with a new one for the current credential hosts. If OS trust included the old one, removes it and trusts the new one, which can show an OS authentication prompt. Running sandboxed tools keep the old certificate authority until they restart. + /// `permissions.locations.*` sub-namespace. + pub fn locations(&self) -> ClientRpcPermissionsLocations<'a> { + ClientRpcPermissionsLocations { + client: self.client, + } + } + + /// Interprets a verbatim managed `permissions` slice into the three policy questions a host asks before granting elevated permissions: whether bypass-permissions mode is forbidden, whether a policy exists that this build could not determine, and whether the layer supplies permission rules the session must enforce. An absent or empty slice means no managed policy applies. The runtime and its permission service share one policy type, so a new managed permission setting is interpreted in exactly one place; a host that reimplements the reading would silently diverge the first time that set grows. Hosts must fail closed on an undetermined policy rather than treating it as permission granted. /// - /// Wire method: `sandbox.proxyCa.rotate`. + /// Wire method: `permissions.evaluateManagedPolicy`. /// /// # Parameters /// - /// * `params` - Identifies the credential hosts that the persistent certificate authority of the sandbox credential proxy must cover. The runtime always adds the hosts from the saved user settings. + /// * `params` - The verbatim managed `permissions` slice to interpret. /// /// # Returns /// - /// Status of the persistent certificate authority of the sandbox credential proxy. + /// The policy questions a host asks before granting elevated permissions. /// ///
/// @@ -3378,29 +3868,40 @@ impl<'a> ClientRpcSandboxProxyCa<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn rotate( + pub(crate) async fn evaluate_managed_policy( &self, - params: SandboxProxyCaRequest, - ) -> Result { + params: PermissionsEvaluateManagedPolicyParams, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SANDBOX_PROXYCA_ROTATE, Some(wire_params)) + .call( + rpc_methods::PERMISSIONS_EVALUATEMANAGEDPOLICY, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } +} - /// Adds the persistent certificate authority of the sandbox credential proxy to OS trust, so sandboxed clients that read only OS trust accept the proxy. Call create first. Refuses a certificate authority that is not constrained to the current credential hosts. Can show an OS authentication prompt. +/// `permissions.folderTrust.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcPermissionsFolderTrust<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcPermissionsFolderTrust<'a> { + /// Reports whether a folder is trusted, reading the persisted trusted-folders list directly rather than through a session. Use this from a host that has no session yet, such as a one-shot command deciding whether to honor a checkout's own settings. It differs from the session-scoped check in two ways that matter: it takes the configuration directory to read the list from, instead of inheriting the session's, and it answers only from the persisted list, so trust a user granted for the lifetime of one session is not visible here. A host that has a session should prefer the session-scoped check, which also covers session-only trust. /// - /// Wire method: `sandbox.proxyCa.trust`. + /// Wire method: `permissions.folderTrust.checkTrust`. /// /// # Parameters /// - /// * `params` - Identifies the credential hosts that the persistent certificate authority of the sandbox credential proxy must cover. The runtime always adds the hosts from the saved user settings. + /// * `params` - A folder to check against a persisted trusted-folders list, and the configuration directory holding that list. /// /// # Returns /// - /// Status of the persistent certificate authority of the sandbox credential proxy. + /// Folder trust check result. /// ///
/// @@ -3409,25 +3910,36 @@ impl<'a> ClientRpcSandboxProxyCa<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn trust( + pub(crate) async fn check_trust( &self, - params: SandboxProxyCaRequest, - ) -> Result { + params: FolderTrustIsTrustedParams, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SANDBOX_PROXYCA_TRUST, Some(wire_params)) + .call( + rpc_methods::PERMISSIONS_FOLDERTRUST_CHECKTRUST, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } +} - /// Removes the persistent certificate authority of the sandbox credential proxy from OS trust. Keeps the stored certificate authority. Can show an OS authentication prompt. Sandboxed clients that read only OS trust then reject the proxy; clients that read the per-process certificate bundle continue to work. +/// `permissions.locations.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcPermissionsLocations<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcPermissionsLocations<'a> { + /// Drops stored per-location permission grants whose directory no longer exists, and reports which location keys were removed. Runs against the permission store under the runtime's own Copilot home, the same store `session.permissions.locations.resolve` reads. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract; it is migration plumbing, not a surface consumers are meant to depend on. It can become public once an SDK consumer needs to prune its own permission store. /// - /// Wire method: `sandbox.proxyCa.remove`. + /// Wire method: `permissions.locations.cleanupOrphaned`. /// /// # Returns /// - /// Status of the persistent certificate authority of the sandbox credential proxy. + /// Which stored location permissions were dropped because their directory is gone. /// ///
/// @@ -3436,34 +3948,37 @@ impl<'a> ClientRpcSandboxProxyCa<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn remove(&self) -> Result { + pub(crate) async fn cleanup_orphaned(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::SANDBOX_PROXYCA_REMOVE, Some(wire_params)) + .call( + rpc_methods::PERMISSIONS_LOCATIONS_CLEANUPORPHANED, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } } -/// `secrets.*` RPCs. +/// `persistence.*` RPCs. #[derive(Clone, Copy)] -pub struct ClientRpcSecrets<'a> { +pub struct ClientRpcPersistence<'a> { pub(crate) client: &'a Client, } -impl<'a> ClientRpcSecrets<'a> { - /// Registers secret values for redaction in session logs and exports. The SDK calls this to inject dynamically generated secret values (e.g., OIDC tokens). +impl<'a> ClientRpcPersistence<'a> { + /// Atomically replaces a JSON file's entire contents while holding the runtime's per-path lock. The lock lives in the runtime process, so a host that writes these files itself takes no part in it and can tear a concurrent runtime write; route the write here instead. Unlike a merging write this never reads or parses the existing file, so a previously corrupted snapshot is replaced wholesale rather than failing every later write. Use it only for callers that always persist a complete snapshot. /// - /// Wire method: `secrets.addFilterValues`. + /// Wire method: `persistence.writeRawJsonLocked`. /// /// # Parameters /// - /// * `params` - Secret values to add to the redaction filter. + /// * `params` - A complete JSON snapshot to write under the runtime's per-path file lock. /// /// # Returns /// - /// Confirmation that the secret values were registered. + /// The outcome of a locked snapshot write. /// ///
/// @@ -3472,37 +3987,50 @@ impl<'a> ClientRpcSecrets<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn add_filter_values( + pub(crate) async fn write_raw_json_locked( &self, - params: SecretsAddFilterValuesRequest, - ) -> Result { + params: PersistenceWriteRawJsonLockedRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SECRETS_ADDFILTERVALUES, Some(wire_params)) + .call( + rpc_methods::PERSISTENCE_WRITERAWJSONLOCKED, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } } -/// `sessionFs.*` RPCs. +/// `plugins.*` RPCs. #[derive(Clone, Copy)] -pub struct ClientRpcSessionFs<'a> { +pub struct ClientRpcPlugins<'a> { pub(crate) client: &'a Client, } -impl<'a> ClientRpcSessionFs<'a> { - /// Registers an SDK client as the session filesystem provider. - /// - /// Wire method: `sessionFs.setProvider`. - /// - /// # Parameters +impl<'a> ClientRpcPlugins<'a> { + /// `plugins.builtin.*` sub-namespace. + pub fn builtin(&self) -> ClientRpcPluginsBuiltin<'a> { + ClientRpcPluginsBuiltin { + client: self.client, + } + } + + /// `plugins.marketplaces.*` sub-namespace. + pub fn marketplaces(&self) -> ClientRpcPluginsMarketplaces<'a> { + ClientRpcPluginsMarketplaces { + client: self.client, + } + } + + /// Lists plugins installed in user/global state. /// - /// * `params` - Initial working directory, session-state path layout, and path conventions used to register the calling SDK client as the session filesystem provider. A registered provider is authoritative for path interpretation and filesystem facts used by workspace permission validation. Paths are interpreted lexically; home-relative paths (`~` and `~/...`) and Windows drive-relative paths such as `C:foo` are unsupported. Until provider-side canonicalization is supported, providers must not expose symlinks inside allowed roots that escape those roots. + /// Wire method: `plugins.list`. /// /// # Returns /// - /// Indicates whether the calling client was registered as the session filesystem provider. + /// Plugins installed in user/global state. /// ///
/// @@ -3511,33 +4039,26 @@ impl<'a> ClientRpcSessionFs<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn set_provider( - &self, - params: SessionFsSetProviderRequest, - ) -> Result { - let wire_params = serde_json::to_value(params)?; + pub async fn list(&self) -> Result { + let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::SESSIONFS_SETPROVIDER, Some(wire_params)) + .call(rpc_methods::PLUGINS_LIST, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } -} - -/// `sessions.*` RPCs. -#[derive(Clone, Copy)] -pub struct ClientRpcSessions<'a> { - pub(crate) client: &'a Client, -} -impl<'a> ClientRpcSessions<'a> { - /// Creates or resumes a local session and returns the opened session ID. + /// Installs a plugin from a marketplace, GitHub repo, URL, or local path. /// - /// Wire method: `sessions.open`. + /// Wire method: `plugins.install`. + /// + /// # Parameters + /// + /// * `params` - Plugin source and optional working directory for relative-path resolution. /// /// # Returns /// - /// Result of opening a session. + /// Result of installing a plugin. /// ///
/// @@ -3546,26 +4067,25 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn open(&self) -> Result { - let wire_params = serde_json::json!({}); + pub async fn install( + &self, + params: PluginsInstallRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_OPEN, Some(wire_params)) + .call(rpc_methods::PLUGINS_INSTALL, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Creates a new session by forking persisted history from an existing session. + /// Uninstalls an installed plugin. /// - /// Wire method: `sessions.fork`. + /// Wire method: `plugins.uninstall`. /// /// # Parameters /// - /// * `params` - Source session identifier to fork from, optional event-ID boundary, and optional friendly name for the new session. - /// - /// # Returns - /// - /// Identifier and optional friendly name assigned to the newly forked session. + /// * `params` - Name (or spec) of the plugin to uninstall. /// ///
/// @@ -3574,26 +4094,26 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn fork(&self, params: SessionsForkRequest) -> Result { + pub async fn uninstall(&self, params: PluginsUninstallRequest) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_FORK, Some(wire_params)) + .call(rpc_methods::PLUGINS_UNINSTALL, Some(wire_params)) .await?; - Ok(serde_json::from_value(_value)?) + Ok(()) } - /// Connects to an existing remote session and exposes it as an SDK session. + /// Updates an installed plugin to its latest published version. /// - /// Wire method: `sessions.connect`. + /// Wire method: `plugins.update`. /// /// # Parameters /// - /// * `params` - Remote session connection parameters. + /// * `params` - Name (or spec) of the plugin to update. /// /// # Returns /// - /// Remote session connection result. + /// Result of updating a single plugin. /// ///
/// @@ -3602,25 +4122,22 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn connect( - &self, - params: ConnectRemoteSessionParams, - ) -> Result { + pub async fn update(&self, params: PluginsUpdateRequest) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_CONNECT, Some(wire_params)) + .call(rpc_methods::PLUGINS_UPDATE, 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.). + /// Updates every installed plugin to its latest published version. /// - /// Wire method: `sessions.list`. + /// Wire method: `plugins.updateAll`. /// /// # Returns /// - /// Sessions matching the filter, ordered most-recently-modified first. + /// Result of updating all installed plugins. /// ///
/// @@ -3629,26 +4146,22 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn list(&self) -> Result { + pub async fn update_all(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::SESSIONS_LIST, Some(wire_params)) + .call(rpc_methods::PLUGINS_UPDATEALL, 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.). + /// Enables installed plugins for new sessions. /// - /// Wire method: `sessions.list`. + /// Wire method: `plugins.enable`. /// /// # Parameters /// - /// * `params` - Optional source filter, metadata-load limit, and context filter applied to the returned sessions. - /// - /// # Returns - /// - /// Sessions matching the filter, ordered most-recently-modified first. + /// * `params` - Plugin names (or specs) to enable, plus the optional working directory the repository-controlled guard is evaluated against. /// ///
/// @@ -3657,29 +4170,22 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn list_with_params( - &self, - params: SessionsListRequest, - ) -> Result { + pub async fn enable(&self, params: PluginsEnableRequest) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_LIST, Some(wire_params)) + .call(rpc_methods::PLUGINS_ENABLE, Some(wire_params)) .await?; - Ok(serde_json::from_value(_value)?) + Ok(()) } - /// Reads lightweight persisted metadata for one local session without opening it. + /// Disables installed plugins for new sessions. /// - /// Wire method: `sessions.getMetadata`. + /// Wire method: `plugins.disable`. /// /// # Parameters /// - /// * `params` - Session ID whose persisted metadata should be read. - /// - /// # Returns - /// - /// Persisted local session metadata when the session exists. + /// * `params` - Plugin names (or specs) to disable, plus the optional working directory the repository-controlled guard is evaluated against. /// ///
/// @@ -3688,29 +4194,30 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub(crate) async fn get_metadata( - &self, - params: SessionsGetMetadataRequest, - ) -> Result { + pub async fn disable(&self, params: PluginsDisableRequest) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_GETMETADATA, Some(wire_params)) + .call(rpc_methods::PLUGINS_DISABLE, Some(wire_params)) .await?; - Ok(serde_json::from_value(_value)?) + Ok(()) } +} - /// Reads client-owned metadata for multiple persisted local sessions without opening them. Results preserve request order and report missing, corrupt, unsupported, or temporarily unavailable sessions independently. +/// `plugins.builtin.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcPluginsBuiltin<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcPluginsBuiltin<'a> { + /// Replaces this server's trusted built-in plugin directories while no sessions are active. /// - /// Wire method: `sessions.getClientMetadata`. + /// Wire method: `plugins.builtin.set`. /// /// # Parameters /// - /// * `params` - Bounded batch request for client-owned metadata from persisted local sessions. - /// - /// # Returns - /// - /// Ordered client metadata outcomes for the requested local sessions. + /// * `params` - Trusted built-in plugin directories to use for this runtime process. /// ///
/// @@ -3719,29 +4226,30 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn get_client_metadata( - &self, - params: SessionsGetClientMetadataRequest, - ) -> Result { + pub async fn set(&self, params: PluginsBuiltinSetRequest) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_GETCLIENTMETADATA, Some(wire_params)) + .call(rpc_methods::PLUGINS_BUILTIN_SET, Some(wire_params)) .await?; - Ok(serde_json::from_value(_value)?) + Ok(()) } +} - /// Reads a page of durable events directly from a local session's persisted journal without creating, resuming, or activating the session. The first read pins the currently opened journal generation and its byte-length boundary; opaque cursor continuations remain on that generation across runtime-owned compaction, truncation, and rewrite operations, which replace the live path atomically, and events appended after the boundary are excluded. For cold hydration, await the first successful page before activation and establish lossless live-event buffering before resume; merge subsequent live events by ID, preserving persisted order and letting live payloads win. Continuations are process-local, single-use capabilities bound to the originating session and storage context and must be paged sequentially; concurrent or repeated use of the same cursor expires that duplicate read rather than reading the generation twice. A complete snapshot has cursorStatus 'ok' and hasMore false. Snapshots expire after five idle minutes, with at most eight retained per process and idle-only eviction under pressure; completion and cancelled-worker exit release their handles. No transcript copy is created, but retained handles may keep replaced files' disk blocks alive until release. Pages have a soft 1 MiB serialized event-array budget including resolved binary assets; one oversized event is returned alone to guarantee progress. Working memory also includes a record/lookahead and asset resolution; resolving the first binary reference may scan the full pinned generation to build a bounded offset index. If the snapshot expires, is evicted, is cancelled before a continuation is established, or becomes unreadable after an observable unsupported in-place shortening, the continuation returns cursorStatus 'expired' with an empty terminal page and never falls back to a different generation. A missing or initially unreadable journal is an RPC error. Persisted history excludes ephemeral events and may omit payloads that are reconstructed only for an active session; use the active session event stream for post-resume live events. - /// - /// Wire method: `sessions.readPersistedEvents`. - /// - /// # Parameters +/// `plugins.marketplaces.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcPluginsMarketplaces<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcPluginsMarketplaces<'a> { + /// Lists all registered marketplaces (defaults + user-added). /// - /// * `params` - Pagination options for reading an inactive or active local session's persisted event journal. + /// Wire method: `plugins.marketplaces.list`. /// /// # Returns /// - /// Batch of session events returned by a read, with cursor and continuation metadata. + /// All registered marketplaces, including built-in defaults. /// ///
/// @@ -3750,29 +4258,26 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn read_persisted_events( - &self, - params: SessionsReadPersistedEventsRequest, - ) -> Result { - let wire_params = serde_json::to_value(params)?; + pub async fn list(&self) -> Result { + let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::SESSIONS_READPERSISTEDEVENTS, Some(wire_params)) + .call(rpc_methods::PLUGINS_MARKETPLACES_LIST, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Lists recent local session IDs that contain user-visible history, omitting housekeeping-only sessions. + /// Registers a new marketplace from a source (owner/repo, URL, or local path). /// - /// Wire method: `sessions.listNonEmptySessionIds`. + /// Wire method: `plugins.marketplaces.add`. /// /// # Parameters /// - /// * `params` - Limit for non-empty local session IDs. + /// * `params` - Marketplace source and optional working directory for relative-path resolution. /// /// # Returns /// - /// Recent local session IDs that contain user-visible history. + /// Result of registering a new marketplace. /// ///
/// @@ -3781,32 +4286,29 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub(crate) async fn list_non_empty_session_ids( + pub async fn add( &self, - params: SessionsListNonEmptySessionIdsRequest, - ) -> Result { + params: PluginsMarketplacesAddRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call( - rpc_methods::SESSIONS_LISTNONEMPTYSESSIONIDS, - Some(wire_params), - ) + .call(rpc_methods::PLUGINS_MARKETPLACES_ADD, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Finds the local session bound to a GitHub task ID, if any. + /// 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: `sessions.findByTaskId`. + /// Wire method: `plugins.marketplaces.remove`. /// /// # Parameters /// - /// * `params` - GitHub task ID to look up. + /// * `params` - Name of the marketplace to remove and an optional force flag. /// /// # Returns /// - /// ID of the local session bound to the given GitHub task, or omitted when none. + /// Outcome of the remove attempt, including dependent-plugin info when applicable. /// ///
/// @@ -3815,29 +4317,1995 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn find_by_task_id( + pub async fn remove( &self, - params: SessionsFindByTaskIDRequest, - ) -> Result { + params: PluginsMarketplacesRemoveRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_FINDBYTASKID, Some(wire_params)) + .call(rpc_methods::PLUGINS_MARKETPLACES_REMOVE, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Resolves a UUID prefix to a unique session ID, if exactly one session matches. + /// Lists plugins advertised by a registered marketplace. + /// + /// Wire method: `plugins.marketplaces.browse`. + /// + /// # Parameters + /// + /// * `params` - Name of the marketplace whose plugin catalog to fetch. + /// + /// # Returns + /// + /// Plugins advertised by the marketplace. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. + /// + /// # Returns + /// + /// Result of refreshing one or more marketplace catalogs. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. + /// + /// # Parameters + /// + /// * `params` - Optional marketplace name; omit to refresh all. + /// + /// # Returns + /// + /// Result of refreshing one or more marketplace catalogs. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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)?) + } +} + +/// `repo.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcRepo<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcRepo<'a> { + /// `repo.settings.*` sub-namespace. + pub fn settings(&self) -> ClientRpcRepoSettings<'a> { + ClientRpcRepoSettings { + client: self.client, + } + } +} + +/// `repo.settings.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcRepoSettings<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcRepoSettings<'a> { + /// Atomically edits one allowed top-level repository setting. Reads the raw file, preserves unrelated keys, applies one scalar or list edit, validates the result and writes under one file lock, so concurrent sibling or list edits are not lost. A malformed or unsafe existing file returns `invalid` without a write; rejected keys, invalid values and I/O errors reject the call. + /// + /// Wire method: `repo.settings.edit`. + /// + /// # Parameters + /// + /// * `params` - One atomic edit of an allowed top-level repository setting. + /// + /// # Returns + /// + /// Outcome of an atomic user or repository settings edit. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn edit( + &self, + params: RepoSettingsEditRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::REPO_SETTINGS_EDIT, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Reads one repository settings file without settings normalization or layering. The existing JSONC parser accepts comments and trailing commas and applies its prototype-safety rules. Missing or blank files return an empty valid config. Invalid files return an empty config with invalid=true; other read failures reject. + /// + /// Wire method: `repo.settings.readRaw`. + /// + /// # Parameters + /// + /// * `params` - Which repository settings file to read. + /// + /// # Returns + /// + /// Parsed repository settings without normalization or layering. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn read_raw( + &self, + params: RepoSettingsReadRawRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::REPO_SETTINGS_READRAW, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Writes or removes one allowed top-level repository setting through the existing validator and file lock. Preserves unrelated stored keys. Invalid keys, values, existing documents, and I/O errors reject without replacing the file. Deletion requires delete=true; otherwise value must be present. + /// + /// Wire method: `repo.settings.writeKey`. + /// + /// # Parameters + /// + /// * `params` - One key to write or remove in a repository settings file. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn write_key(&self, params: RepoSettingsWriteKeyRequest) -> Result<(), Error> { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::REPO_SETTINGS_WRITEKEY, Some(wire_params)) + .await?; + Ok(()) + } + + /// Replaces the repository-overridable settings in one locked write. Allowed keys present in settings are validated and written; allowed keys absent from settings are removed. Other input keys are ignored, and unknown keys already stored in the file remain unchanged. Invalid values or an invalid existing file reject without a partial write. + /// + /// Wire method: `repo.settings.writeAll`. + /// + /// # Parameters + /// + /// * `params` - The complete set of repository-overridable settings to store. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn write_all(&self, params: RepoSettingsWriteAllRequest) -> Result<(), Error> { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::REPO_SETTINGS_WRITEALL, Some(wire_params)) + .await?; + Ok(()) + } +} + +/// `runtime.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcRuntime<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcRuntime<'a> { + /// Gracefully shuts down an SDK-owned runtime. The response is sent only after cleanup completes; callers may then terminate the owned runtime process. + /// + /// Wire method: `runtime.shutdown`. + /// + ///
+ /// + /// **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. + /// + ///
+ pub async fn shutdown(&self) -> Result<(), Error> { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::RUNTIME_SHUTDOWN, Some(wire_params)) + .await?; + Ok(()) + } +} + +/// `sandbox.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcSandbox<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcSandbox<'a> { + /// `sandbox.proxyCa.*` sub-namespace. + pub fn proxy_ca(&self) -> ClientRpcSandboxProxyCa<'a> { + ClientRpcSandboxProxyCa { + client: self.client, + } + } + + /// `sandbox.proxySecret.*` sub-namespace. + pub fn proxy_secret(&self) -> ClientRpcSandboxProxySecret<'a> { + ClientRpcSandboxProxySecret { + client: self.client, + } + } + + /// `sandbox.settings.*` sub-namespace. + pub fn settings(&self) -> ClientRpcSandboxSettings<'a> { + ClientRpcSandboxSettings { + client: self.client, + } + } + + /// Raises a user's sandbox settings to an administrator's managed sandbox floor and returns the result a session should actually run under. The floor can only tighten: an enterprise force-on is reflected even when the user's own `enabled` is false, and a user cannot lower a setting the floor raised. The result is normalized through the declared sandbox shape, so a key reorder or an undeclared passthrough key is not mistaken for a policy change by callers that diff two results. A host must call this rather than merge the two layers itself, because reimplementing the direction of the fold is what turns an administrator's floor into a suggestion. + /// + /// Wire method: `sandbox.resolveFlooredConfig`. + /// + /// # Parameters + /// + /// * `params` - The user's sandbox settings and the managed sandbox floor to raise them to. + /// + /// # Returns + /// + /// The user's sandbox settings raised to the managed floor. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn resolve_floored_config( + &self, + params: SandboxResolveFlooredConfigParams, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SANDBOX_RESOLVEFLOOREDCONFIG, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Builds the effective filesystem policy one `@`-file picker checks its suggestions against, so Git discovery and grant canonicalization run once for the picker's lifetime instead of on every debounced keystroke. Returns no policy when the sandbox is absent or disabled, which the paired filter reads as "keep every candidate". A host must pair this with `sandbox.filterPickerCandidates` rather than resolve a grant list itself, because the picker's grants are narrower than a file tool's — the same path a built-in tool may read can be one the picker must not offer. + /// + /// Wire method: `sandbox.preparePickerPolicy`. + /// + /// # Parameters + /// + /// * `params` - The working directory and sandbox settings to build a file-picker policy from. + /// + /// # Returns + /// + /// The prepared file-picker policy, or none when the sandbox is disabled. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn prepare_picker_policy( + &self, + params: SandboxPreparePickerPolicyParams, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SANDBOX_PREPAREPICKERPOLICY, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Checks a batch of `@`-file picker suggestions against a policy prepared by `sandbox.preparePickerPolicy` and returns one allow decision per candidate, positionally aligned with the request. Batched deliberately: each check canonicalizes a path component-by-component through the real filesystem, so one call amortizes that over a single hop rather than dozens while the user types. A directory the policy does not itself allow can still be kept as a waypoint to an allowed subtree. An absent policy keeps every candidate. + /// + /// Wire method: `sandbox.filterPickerCandidates`. + /// + /// # Parameters + /// + /// * `params` - A batch of file-picker candidates to check against a prepared policy. + /// + /// # Returns + /// + /// Per-candidate allow decisions, positionally aligned with the request. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn filter_picker_candidates( + &self, + params: SandboxFilterPickerCandidatesParams, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::SANDBOX_FILTERPICKERCANDIDATES, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// The sandbox a single named feature actually launches under, which is not the same as the session's sandbox. A feature is routed out of the sandbox by its own flag — `sandboxMcpServers`, `sandboxLspServers` — so an enabled session can still start one server confined and another free. Returns a disabled sandbox rather than an error when the host has no sandbox slice at all, so an absent config and an opted-out feature read alike at the launch site. A host must ask per feature rather than reuse the session config, because reading only `enabled` confines a server the user opted out of, or reports as sandboxed one that is not. + /// + /// Wire method: `sandbox.resolveForOptional`. + /// + /// # Parameters + /// + /// * `params` - A sandbox config and the per-feature routing flag to resolve it for. + /// + /// # Returns + /// + /// The sandbox a named feature actually launches under. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn resolve_for_optional( + &self, + params: SandboxEffectiveForOptionalParams, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SANDBOX_RESOLVEFOROPTIONAL, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Reports whether this machine can run an OS-level command sandbox at all. A host must ask before it offers sandboxing, because the answer is a property of the machine rather than of any setting: on Linux it depends on bubblewrap being installed, and on Windows on the OS build supporting BaseContainer. Probing it requires spawning a process the first time, so the answer is cached for the life of the runtime. A host that assumes support and enables the sandbox anyway produces a session in which every shell command fails. + /// + /// Wire method: `sandbox.checkPlatformSupport`. + /// + /// # Returns + /// + /// True when an OS-level sandbox can run on this host. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn check_platform_support( + &self, + ) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::SANDBOX_CHECKPLATFORMSUPPORT, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Reports whether this Windows host's BaseContainer implementation can reach the host's loopback interface directly, which is what a local-network policy needs to avoid routing through the egress proxy. Like `sandbox.checkPlatformSupport`, the answer is a property of the machine's OS build rather than of any setting, and resolving it the first time spawns a platform probe. A host that offers the direct local-network option on a machine that lacks the capability produces a session whose sandboxed commands cannot reach local services. Always false off Windows. + /// + /// Wire method: `sandbox.checkWindowsHostLoopbackSupport`. + /// + /// # Returns + /// + /// True when the sandbox on this host can reach host loopback directly. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn check_windows_host_loopback_support( + &self, + ) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call( + rpc_methods::SANDBOX_CHECKWINDOWSHOSTLOOPBACKSUPPORT, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// The explanation a host shows when it refuses an explicit request to turn the sandbox on for a machine that cannot run one. Carries the platform-specific remedy — install bubblewrap, update Windows — and the documentation link, so a host that got `false` from `sandbox.checkPlatformSupport` does not have to compose its own reason and cannot describe the same machine differently from the runtime's own spawn path. + /// + /// Wire method: `sandbox.getUnsupportedMessage`. + /// + /// # Returns + /// + /// The user-facing refusal, including the platform remedy and the documentation link. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn get_unsupported_message( + &self, + ) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call( + rpc_methods::SANDBOX_GETUNSUPPORTEDMESSAGE, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// The notice a host shows when a stored `sandbox` setting is being ignored for this session because the machine cannot enforce it. Distinct from `sandbox.getUnsupportedMessage`, which refuses an explicit request: this one reports that a setting the user already saved is inert, states that commands will run unsandboxed, and confirms the stored setting itself is left alone. A host that stays silent here leaves the user believing a sandbox is active. + /// + /// Wire method: `sandbox.getUnsupportedSettingDisabledNotice`. + /// + /// # Returns + /// + /// The user-facing notice that a saved sandbox setting is inert for this session. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn get_unsupported_setting_disabled_notice( + &self, + ) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call( + rpc_methods::SANDBOX_GETUNSUPPORTEDSETTINGDISABLEDNOTICE, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// The warning a host prints at startup when a session asked for a sandbox on a machine that cannot run one. The third of the unsupported notices, and the only one that depends on the policy in force: it names the remedy the reader can actually act on, which is the stored setting for an unmanaged host, an administrator for a managed one, and waiting for the policy to resolve when the sandbox stayed on only because the policy could not be determined. Returns no notice when the machine does support a sandbox, so a host can call it unconditionally on the startup path. A host that composes its own warning tells a managed user to change a setting their policy will overwrite. + /// + /// Wire method: `sandbox.getUnsupportedStartupNotice`. + /// + /// # Parameters + /// + /// * `params` - The managed sandbox policy in force, which selects which remedy the notice offers. + /// + /// # Returns + /// + /// The startup warning, absent when the machine can run a sandbox. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn get_unsupported_startup_notice( + &self, + params: SandboxUnsupportedStartupNoticeParams, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::SANDBOX_GETUNSUPPORTEDSTARTUPNOTICE, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Whether one slash command stays reachable while sandbox enforcement is unavailable and the session is otherwise blocked. The permitted set is deliberately narrow — commands that only report state, and the few that let the user repair or leave the situation — and it is keyed on the arguments as well as the name, so `/sandbox status` is admitted where `/sandbox enable` is not. A host must ask rather than keep its own list: a command this returns false for is one the runtime would refuse anyway, and a divergent list either strands the user with no way out or admits a command that acts on an unenforced session. + /// + /// Wire method: `sandbox.checkEnforcementCommand`. + /// + /// # Parameters + /// + /// * `params` - One slash command, as typed, to test against the enforcement-block allowlist. + /// + /// # Returns + /// + /// True when the command stays available while enforcement is blocked. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn check_enforcement_command( + &self, + params: SandboxEnforcementCommandAllowedParams, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::SANDBOX_CHECKENFORCEMENTCOMMAND, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Returns the documentation URL a host should point a user at when it declines or downgrades a sandbox setting. The runtime owns the address so every surface cites the same page and a host does not hard-code a link that later moves. + /// + /// Wire method: `sandbox.getDocsUrl`. + /// + /// # Returns + /// + /// The documentation URL a host cites when it declines or downgrades a sandbox setting. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn get_docs_url(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::SANDBOX_GETDOCSURL, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Reports whether the host running this runtime can run the command sandbox, without starting a session or spawning a sandboxed command. + /// + /// Wire method: `sandbox.getHostSupport`. + /// + /// # Returns + /// + /// Whether the host running this runtime can run the command sandbox. The runtime checks `supported` once per process. A capability answer can change while the process runs, for example after the user installs a missing package. + /// + ///
+ /// + /// **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. + /// + ///
+ pub async fn get_host_support(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::SANDBOX_GETHOSTSUPPORT, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } +} + +/// `sandbox.proxyCa.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcSandboxProxyCa<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcSandboxProxyCa<'a> { + /// Reports whether this runtime process can offer installing the sandbox credential-proxy certificate authority into OS trust. + /// + /// Wire method: `sandbox.proxyCa.checkInstallationSupport`. + /// + /// # Returns + /// + /// True when this process can offer installing the sandbox credential-proxy certificate authority into OS trust. + /// + ///
+ /// + /// **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. + /// + ///
+ pub async fn check_installation_support( + &self, + ) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call( + rpc_methods::SANDBOX_PROXYCA_CHECKINSTALLATIONSUPPORT, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Reports whether this sandbox configuration uses the credential proxy and can need certificate-authority setup. + /// + /// Wire method: `sandbox.proxyCa.checkRelevance`. + /// + /// # Parameters + /// + /// * `params` - Identifies the credential hosts that the persistent certificate authority of the sandbox credential proxy must cover. The runtime always adds the hosts from the saved user settings. + /// + /// # Returns + /// + /// True when the sandbox configuration uses the credential proxy and can need certificate-authority setup. + /// + ///
+ /// + /// **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. + /// + ///
+ pub async fn check_relevance( + &self, + params: SandboxProxyCaRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::SANDBOX_PROXYCA_CHECKRELEVANCE, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Reports whether the persistent certificate authority of the sandbox credential proxy exists, whether OS trust includes it, and whether it must be rotated. Changes nothing. + /// + /// Wire method: `sandbox.proxyCa.getStatus`. + /// + /// # Parameters + /// + /// * `params` - Identifies the credential hosts that the persistent certificate authority of the sandbox credential proxy must cover. The runtime always adds the hosts from the saved user settings. + /// + /// # Returns + /// + /// Status of the persistent certificate authority of the sandbox credential proxy. + /// + ///
+ /// + /// **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. + /// + ///
+ pub async fn get_status( + &self, + params: SandboxProxyCaRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SANDBOX_PROXYCA_GETSTATUS, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Creates the persistent certificate authority of the sandbox credential proxy if none is stored, without changing OS trust, and returns the path of its public certificate. Keeps an existing certificate authority, even one that must be rotated. Fails where OS trust is unsupported. Trust it with sandbox.proxyCa.trust: the CLI trusts only the hosts in the saved user settings, so it refuses a certificate authority that also covers hosts from sandboxConfig. + /// + /// Wire method: `sandbox.proxyCa.create`. + /// + /// # Parameters + /// + /// * `params` - Identifies the credential hosts that the persistent certificate authority of the sandbox credential proxy must cover. The runtime always adds the hosts from the saved user settings. + /// + /// # Returns + /// + /// Result of creating the persistent certificate authority of the sandbox credential proxy. + /// + ///
+ /// + /// **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. + /// + ///
+ pub async fn create( + &self, + params: SandboxProxyCaRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SANDBOX_PROXYCA_CREATE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Replaces the persistent certificate authority of the sandbox credential proxy with a new one for the current credential hosts. If OS trust included the old one, removes it and trusts the new one, which can show an OS authentication prompt. Running sandboxed tools keep the old certificate authority until they restart. + /// + /// Wire method: `sandbox.proxyCa.rotate`. + /// + /// # Parameters + /// + /// * `params` - Identifies the credential hosts that the persistent certificate authority of the sandbox credential proxy must cover. The runtime always adds the hosts from the saved user settings. + /// + /// # Returns + /// + /// Status of the persistent certificate authority of the sandbox credential proxy. + /// + ///
+ /// + /// **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. + /// + ///
+ pub async fn rotate( + &self, + params: SandboxProxyCaRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SANDBOX_PROXYCA_ROTATE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Adds the persistent certificate authority of the sandbox credential proxy to OS trust, so sandboxed clients that read only OS trust accept the proxy. Call create first. Refuses a certificate authority that is not constrained to the current credential hosts. Can show an OS authentication prompt. + /// + /// Wire method: `sandbox.proxyCa.trust`. + /// + /// # Parameters + /// + /// * `params` - Identifies the credential hosts that the persistent certificate authority of the sandbox credential proxy must cover. The runtime always adds the hosts from the saved user settings. + /// + /// # Returns + /// + /// Status of the persistent certificate authority of the sandbox credential proxy. + /// + ///
+ /// + /// **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. + /// + ///
+ pub async fn trust( + &self, + params: SandboxProxyCaRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SANDBOX_PROXYCA_TRUST, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Removes the persistent certificate authority of the sandbox credential proxy from OS trust. Keeps the stored certificate authority. Can show an OS authentication prompt. Sandboxed clients that read only OS trust then reject the proxy; clients that read the per-process certificate bundle continue to work. + /// + /// Wire method: `sandbox.proxyCa.remove`. + /// + /// # Returns + /// + /// Status of the persistent certificate authority of the sandbox credential proxy. + /// + ///
+ /// + /// **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. + /// + ///
+ pub async fn remove(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::SANDBOX_PROXYCA_REMOVE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } +} + +/// `sandbox.proxySecret.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcSandboxProxySecret<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcSandboxProxySecret<'a> { + /// Reports where this runtime keeps the sandbox proxy password: the credential store directory and the entry identifier within it. Both are derived from the configuration directory this runtime was started with, so a host does not resolve them itself. Read this to recognize a `${secret:…}` value in stored sandbox settings as this runtime's own proxy password rather than a reference the user wrote by hand. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract. + /// + /// Wire method: `sandbox.proxySecret.getLocation`. + /// + /// # Returns + /// + /// Where this runtime keeps the sandbox proxy password. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn get_location(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call( + rpc_methods::SANDBOX_PROXYSECRET_GETLOCATION, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Reads the stored sandbox proxy password from the credential store `sandbox.proxySecret.getLocation` names, preferring the OS keychain and falling back to the on-disk store. Reports an absent value rather than failing when no entry exists. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract. + /// + /// Wire method: `sandbox.proxySecret.get`. + /// + /// # Returns + /// + /// The stored sandbox proxy password, if one is present. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn get(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::SANDBOX_PROXYSECRET_GET, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Stores the sandbox proxy password in the credential store `sandbox.proxySecret.getLocation` names, replacing any existing entry. Reports whether the OS keychain accepted it or it fell back to the on-disk store. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract. + /// + /// Wire method: `sandbox.proxySecret.save`. + /// + /// # Parameters + /// + /// * `params` - The sandbox proxy password to store. + /// + /// # Returns + /// + /// Where the sandbox proxy password was stored. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn save( + &self, + params: SandboxProxySecretSaveRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SANDBOX_PROXYSECRET_SAVE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Removes the stored sandbox proxy password from the credential store `sandbox.proxySecret.getLocation` names. Succeeds when no entry exists, so it is safe to call while clearing a proxy password whose origin is unknown. Marked internal because it exists to carry a CLI call site off the napi boundary onto the SDK contract. + /// + /// Wire method: `sandbox.proxySecret.delete`. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn delete(&self) -> Result<(), Error> { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::SANDBOX_PROXYSECRET_DELETE, Some(wire_params)) + .await?; + Ok(()) + } +} + +/// `sandbox.settings.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcSandboxSettings<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcSandboxSettings<'a> { + /// Resolves a stored sandbox settings object into the effective one, filling every value the user left unset with its default. Read this to render or reason about the sandbox configuration a session would actually run under, rather than the sparse document the user wrote. + /// + /// Wire method: `sandbox.settings.applyDefaults`. + /// + /// # Parameters + /// + /// * `params` - The stored sandbox settings to resolve against the defaults. + /// + /// # Returns + /// + /// The effective sandbox settings, with every unset value resolved to its default. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn apply_defaults( + &self, + params: SandboxApplyDefaultsRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::SANDBOX_SETTINGS_APPLYDEFAULTS, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } +} + +/// `secrets.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcSecrets<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcSecrets<'a> { + /// Registers secret values for redaction in session logs and exports. The SDK calls this to inject dynamically generated secret values (e.g., OIDC tokens). + /// + /// Wire method: `secrets.addFilterValues`. + /// + /// # Parameters + /// + /// * `params` - Secret values to add to the redaction filter. + /// + /// # Returns + /// + /// Confirmation that the secret values were registered. + /// + ///
+ /// + /// **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. + /// + ///
+ pub async fn add_filter_values( + &self, + params: SecretsAddFilterValuesRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SECRETS_ADDFILTERVALUES, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } +} + +/// `sessionFs.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcSessionFs<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcSessionFs<'a> { + /// Registers an SDK client as the session filesystem provider. + /// + /// Wire method: `sessionFs.setProvider`. + /// + /// # Parameters + /// + /// * `params` - Initial working directory, session-state path layout, and path conventions used to register the calling SDK client as the session filesystem provider. A registered provider is authoritative for path interpretation and filesystem facts used by workspace permission validation. Paths are interpreted lexically; home-relative paths (`~` and `~/...`) and Windows drive-relative paths such as `C:foo` are unsupported. Until provider-side canonicalization is supported, providers must not expose symlinks inside allowed roots that escape those roots. + /// + /// # Returns + /// + /// Indicates whether the calling client was registered as the session filesystem provider. + /// + ///
+ /// + /// **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. + /// + ///
+ pub async fn set_provider( + &self, + params: SessionFsSetProviderRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONFS_SETPROVIDER, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } +} + +/// `sessions.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcSessions<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcSessions<'a> { + /// Creates or resumes a local session and returns the opened session ID. + /// + /// Wire method: `sessions.open`. + /// + /// # Returns + /// + /// Result of opening a session. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. + /// + /// # Parameters + /// + /// * `params` - Source session identifier to fork from, optional event-ID boundary, and optional friendly name for the new session. + /// + /// # Returns + /// + /// Identifier and optional friendly name assigned to the newly forked session. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. + /// + /// # Parameters + /// + /// * `params` - Remote session connection parameters. + /// + /// # Returns + /// + /// Remote session connection result. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. + /// + /// # Returns + /// + /// Sessions matching the filter, ordered most-recently-modified first. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. + /// + /// # Parameters + /// + /// * `params` - Optional source filter, metadata-load limit, and context filter applied to the returned sessions. + /// + /// # Returns + /// + /// Sessions matching the filter, ordered most-recently-modified first. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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)?) + } + + /// Reads lightweight persisted metadata for one local session without opening it. + /// + /// Wire method: `sessions.getMetadata`. + /// + /// # Parameters + /// + /// * `params` - Session ID whose persisted metadata should be read. + /// + /// # Returns + /// + /// Persisted local session metadata when the session exists. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn get_metadata( + &self, + params: SessionsGetMetadataRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_GETMETADATA, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Reads client-owned metadata for multiple persisted local sessions without opening them. Results preserve request order and report missing, corrupt, unsupported, or temporarily unavailable sessions independently. + /// + /// Wire method: `sessions.getClientMetadata`. + /// + /// # Parameters + /// + /// * `params` - Bounded batch request for client-owned metadata from persisted local sessions. + /// + /// # Returns + /// + /// Ordered client metadata outcomes for the requested local sessions. + /// + ///
+ /// + /// **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. + /// + ///
+ pub async fn get_client_metadata( + &self, + params: SessionsGetClientMetadataRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_GETCLIENTMETADATA, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Reads a page of durable events directly from a local session's persisted journal without creating, resuming, or activating the session. The first read pins the currently opened journal generation and its byte-length boundary; opaque cursor continuations remain on that generation across runtime-owned compaction, truncation, and rewrite operations, which replace the live path atomically, and events appended after the boundary are excluded. For cold hydration, await the first successful page before activation and establish lossless live-event buffering before resume; merge subsequent live events by ID, preserving persisted order and letting live payloads win. Continuations are process-local, single-use capabilities bound to the originating session and storage context and must be paged sequentially; concurrent or repeated use of the same cursor expires that duplicate read rather than reading the generation twice. A complete snapshot has cursorStatus 'ok' and hasMore false. Snapshots expire after five idle minutes, with at most eight retained per process and idle-only eviction under pressure; completion and cancelled-worker exit release their handles. No transcript copy is created, but retained handles may keep replaced files' disk blocks alive until release. Pages have a soft 1 MiB serialized event-array budget including resolved binary assets; one oversized event is returned alone to guarantee progress. Working memory also includes a record/lookahead and asset resolution; resolving the first binary reference may scan the full pinned generation to build a bounded offset index. If the snapshot expires, is evicted, is cancelled before a continuation is established, or becomes unreadable after an observable unsupported in-place shortening, the continuation returns cursorStatus 'expired' with an empty terminal page and never falls back to a different generation. A missing or initially unreadable journal is an RPC error. Persisted history excludes ephemeral events and may omit payloads that are reconstructed only for an active session; use the active session event stream for post-resume live events. + /// + /// Wire method: `sessions.readPersistedEvents`. + /// + /// # Parameters + /// + /// * `params` - Pagination options for reading an inactive or active local session's persisted event journal. + /// + /// # Returns + /// + /// Batch of session events returned by a read, with cursor and continuation metadata. + /// + ///
+ /// + /// **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. + /// + ///
+ pub async fn read_persisted_events( + &self, + params: SessionsReadPersistedEventsRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_READPERSISTEDEVENTS, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Lists recent local session IDs that contain user-visible history, omitting housekeeping-only sessions. + /// + /// Wire method: `sessions.listNonEmptySessionIds`. + /// + /// # Parameters + /// + /// * `params` - Limit for non-empty local session IDs. + /// + /// # Returns + /// + /// Recent local session IDs that contain user-visible history. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn list_non_empty_session_ids( + &self, + params: SessionsListNonEmptySessionIdsRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::SESSIONS_LISTNONEMPTYSESSIONIDS, + 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`. + /// + /// # Parameters + /// + /// * `params` - GitHub task ID to look up. + /// + /// # Returns + /// + /// ID of the local session bound to the given GitHub task, or omitted when none. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. /// /// # Parameters /// - /// * `params` - UUID prefix to resolve to a unique session ID. + /// * `params` - UUID prefix to resolve to a unique session ID. + /// + /// # Returns + /// + /// Session ID matching the prefix, omitted when no unique match exists. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. + /// + /// # Parameters + /// + /// * `params` - Optional working-directory context used to score session relevance. + /// + /// # Returns + /// + /// Most-relevant session ID for the supplied context, or omitted when no sessions exist. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. + /// + /// # Parameters + /// + /// * `params` - Session ID whose event-log file path to compute. + /// + /// # Returns + /// + /// Absolute path to the session's events.jsonl file on disk. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn get_event_file_path( + &self, + params: SessionsGetEventFilePathRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_GETEVENTFILEPATH, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Returns the on-disk byte size of each session's workspace directory. + /// + /// Wire method: `sessions.getSizes`. + /// + /// # Returns + /// + /// Map of sessionId -> on-disk size in bytes for each session's workspace directory. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. + /// + /// # Parameters + /// + /// * `params` - Session IDs to test for live in-use locks. + /// + /// # Returns + /// + /// Session IDs from the input set that are currently in use by another process. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. + /// + /// # Parameters + /// + /// * `params` - Session ID to look up the persisted remote-steerable flag for. + /// + /// # Returns + /// + /// The session's persisted remote-steerable flag, or omitted when no value has been persisted. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn get_persisted_remote_steerable( + &self, + params: SessionsGetPersistedRemoteSteerableRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::SESSIONS_GETPERSISTEDREMOTESTEERABLE, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Closes a session: emits shutdown, flushes pending events, releases the in-use lock, and disposes the active session. + /// + /// Wire method: `sessions.close`. + /// + /// # Parameters + /// + /// * `params` - Session ID to close. + /// + /// # Returns + /// + /// 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. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. + /// + /// # Parameters + /// + /// * `params` - Session IDs to close, deactivate, and delete from disk. + /// + /// # Returns + /// + /// Map of sessionId -> bytes freed by removing the session's workspace directory. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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 one local session from disk after running the same lifecycle hooks as the session manager. + /// + /// Wire method: `sessions.delete`. + /// + /// # Parameters + /// + /// * `params` - Session ID to delete from disk. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn delete(&self, params: SessionsDeleteRequest) -> Result<(), Error> { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_DELETE, Some(wire_params)) + .await?; + Ok(()) + } + + /// Deletes sessions older than the given threshold, with optional dry-run and exclusion list. + /// + /// Wire method: `sessions.pruneOld`. + /// + /// # Parameters + /// + /// * `params` - Age threshold and optional flags controlling which old sessions are pruned (or simulated when dryRun is true). + /// + /// # Returns + /// + /// Outcome of the prune operation: deleted IDs, dry-run candidates, skipped IDs, total bytes freed, and the dry-run flag. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. + /// + /// # Parameters + /// + /// * `params` - Session ID whose pending events should be flushed to disk. + /// + /// # Returns + /// + /// Flush a session's pending events to disk. No-op when no writer exists for the session (e.g., already closed). + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. + /// + /// # Parameters + /// + /// * `params` - Session ID whose in-use lock should be released. + /// + /// # Returns + /// + /// 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. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. + /// + /// # Parameters + /// + /// * `params` - Session metadata records to enrich with summary and context information. + /// + /// # Returns + /// + /// The enriched metadata records, with summary and context fields backfilled where available. Sessions confirmed empty and unnamed are omitted. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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)?) + } + + /// Creates the workspace record for a session that has not been opened yet. A host that hands a session off to another application — writing the record and then launching that application against the session ID — needs the record on disk before any session exists to carry it, which the session-scoped workspace methods cannot do. Replaces any existing record and resets the checkpoint index. When writing to the local filesystem, a stored `fork_count` survives on disk. Returns the record it built, so a surviving stored `fork_count` can differ from the answer. + /// + /// Wire method: `sessions.createWorkspace`. + /// + /// # Parameters + /// + /// * `params` - Identity, state location and starting context for a workspace record. + /// + /// # Returns + /// + /// The workspace record that was written. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn create_workspace( + &self, + params: SessionsCreateWorkspaceRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_CREATEWORKSPACE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Reads a session's workspace record straight from disk, without opening the session. Resuming by session ID has to know where the session lives before it can connect, so the lookup cannot come from the session-scoped workspace methods, which resolve their location from a live session's context. Returns no record when the file is absent. + /// + /// Wire method: `sessions.loadWorkspace`. + /// + /// # Parameters + /// + /// * `params` - Where the session's state lives, as a root directory and the session ID under it. + /// + /// # Returns + /// + /// The workspace record on disk, omitted when the session has none. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn load_workspace( + &self, + params: SessionsLoadWorkspaceRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_LOADWORKSPACE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Merges fields into a session's workspace record on disk, creating the record when it is absent. The counterpart to `sessions.loadWorkspace`, for the same before-the-session-exists case. It preserves stored workspace-schema fields the request does not supply, does not preserve stored keys outside the workspace schema, and never replaces a stored `fork_count`. + /// + /// Wire method: `sessions.updateWorkspaceFields`. + /// + /// # Parameters + /// + /// * `params` - Where the session's state lives, plus workspace-schema fields to merge into its workspace record. Stored keys outside the schema are not preserved, and a stored `fork_count` is never replaced. + /// + /// # Returns + /// + /// The merge completed. The record carries the supplied workspace-schema fields, but a stored `fork_count` stays. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn update_workspace_fields( + &self, + params: SessionsUpdateWorkspaceFieldsRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::SESSIONS_UPDATEWORKSPACEFIELDS, + 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`. + /// + /// # Parameters + /// + /// * `params` - Active session ID and an optional flag for deferring repo-level hooks until folder trust. + /// + /// # Returns + /// + /// 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. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. + /// + /// # Parameters + /// + /// * `params` - Active session ID whose deferred repo-level hooks should be loaded. + /// + /// # Returns + /// + /// Queued repo-level startup prompts and the total hook command count after loading. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. + /// + /// # Parameters + /// + /// * `params` - Manager-wide additional plugins to register; replaces any previously-configured set. + /// + /// # Returns + /// + /// 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. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. + /// + /// # Parameters + /// + /// * `params` - Session ID whose board entry count should be returned. /// /// # Returns /// - /// Session ID matching the prefix, omitted when no unique match exists. + /// Dynamic-context board entry count, when available. /// ///
/// @@ -3846,29 +6314,262 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn find_by_prefix( + pub(crate) async fn get_board_entry_count( &self, - params: SessionsFindByPrefixRequest, - ) -> Result { + params: SessionsGetBoardEntryCountRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_FINDBYPREFIX, Some(wire_params)) + .call(rpc_methods::SESSIONS_GETBOARDENTRYCOUNT, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// 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. + /// + /// Wire method: `sessions.startRemoteControl`. + /// + /// # Parameters + /// + /// * `params` - Parameters for attaching the remote-control singleton to a session. + /// + /// # Returns + /// + /// Wrapper for the singleton's current status. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. + /// + /// # Parameters + /// + /// * `params` - Parameters for atomically rebinding the remote-control singleton. + /// + /// # Returns + /// + /// Outcome of a transferRemoteControl call. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. + /// + /// # Parameters + /// + /// * `params` - Patch for the singleton's steering state. + /// + /// # Returns + /// + /// Wrapper for the singleton's current status. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. + /// + /// # Returns + /// + /// Outcome of a stopRemoteControl call. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. + /// + /// # Parameters + /// + /// * `params` - Parameters for stopping the remote-control singleton. + /// + /// # Returns + /// + /// Outcome of a stopRemoteControl call. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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`. + /// + /// # Returns + /// + /// Wrapper for the singleton's current status. + /// + ///
+ /// + /// **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. + /// + ///
+ pub 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)?) } - /// Returns the most-relevant prior session for a given working-directory context. + /// Attaches (or detaches) an in-process ExtensionController delegate for the given session in a local host adapter. Pass `controller: undefined` to detach. Internal because the controller cannot cross the JSON-RPC boundary; the runtime manages its own session extension service. + /// + /// Wire method: `sessions.configureSessionExtensions`. + /// + /// # Parameters + /// + /// * `params` - Params to attach or detach an in-process ExtensionController delegate. + /// + ///
+ /// + /// **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. + /// + ///
+ pub(crate) async fn configure_session_extensions( + &self, + params: ConfigureSessionExtensionsParams, + ) -> Result<(), Error> { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::SESSIONS_CONFIGURESESSIONEXTENSIONS, + Some(wire_params), + ) + .await?; + Ok(()) + } +} + +/// `skills.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcSkills<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcSkills<'a> { + /// `skills.config.*` sub-namespace. + pub fn config(&self) -> ClientRpcSkillsConfig<'a> { + ClientRpcSkillsConfig { + client: self.client, + } + } + + /// `skills.installations.*` sub-namespace. + pub fn installations(&self) -> ClientRpcSkillsInstallations<'a> { + ClientRpcSkillsInstallations { + client: self.client, + } + } + + /// Plans installation of a verified Agent Finder Skill candidate without writing files. The returned review is safe to present to a user and installing always leaves the Skill disabled until separately enabled. /// - /// Wire method: `sessions.getLastForContext`. + /// Wire method: `skills.planInstall`. /// /// # Parameters /// - /// * `params` - Optional working-directory context used to score session relevance. + /// * `params` - Side-effect-free planning of one verified Agent Finder Skill candidate. /// /// # Returns /// - /// Most-relevant session ID for the supplied context, or omitted when no sessions exist. + /// Skill installation management result with the honoured contract, or a typed refusal. /// ///
/// @@ -3877,29 +6578,29 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn get_last_for_context( + pub async fn plan_install( &self, - params: SessionsGetLastForContextRequest, - ) -> Result { + params: SkillPlanInstallRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_GETLASTFORCONTEXT, Some(wire_params)) + .call(rpc_methods::SKILLS_PLANINSTALL, 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. + /// Consumes one verified Skill installation plan, requests explicit human consent through installations.confirm on the original connection, then revalidates and installs the Skill disabled. /// - /// Wire method: `sessions.getEventFilePath`. + /// Wire method: `skills.applyInstall`. /// /// # Parameters /// - /// * `params` - Session ID whose event-log file path to compute. + /// * `params` - Applies exactly one retained verified Skill installation plan. /// /// # Returns /// - /// Absolute path to the session's events.jsonl file on disk. + /// Skill installation result with the honoured contract, or a typed request/negotiation refusal. /// ///
/// @@ -3908,25 +6609,29 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub(crate) async fn get_event_file_path( + pub async fn apply_install( &self, - params: SessionsGetEventFilePathRequest, - ) -> Result { + params: SkillApplyInstallRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_GETEVENTFILEPATH, Some(wire_params)) + .call(rpc_methods::SKILLS_APPLYINSTALL, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Returns the on-disk byte size of each session's workspace directory. + /// Prepares a read-only removal plan for an owned verified Agent Finder Skill installation. Uninstall planning is never gated by the Skill-install feature flag. /// - /// Wire method: `sessions.getSizes`. + /// Wire method: `skills.planUninstall`. + /// + /// # Parameters + /// + /// * `params` - Read-only preparation of one owned Skill removal under fresh selected-session authority. /// /// # Returns /// - /// Map of sessionId -> on-disk size in bytes for each session's workspace directory. + /// Skill installation management result with the honoured contract, or a typed refusal. /// ///
/// @@ -3935,26 +6640,29 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn get_sizes(&self) -> Result { - let wire_params = serde_json::json!({}); + pub async fn plan_uninstall( + &self, + params: SkillPlanUninstallRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_GETSIZES, Some(wire_params)) + .call(rpc_methods::SKILLS_PLANUNINSTALL, 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. + /// Consumes an owned Skill removal plan, requests explicit human consent through installations.confirm, refuses drift, and removes the exact owned files through quarantine. /// - /// Wire method: `sessions.checkInUse`. + /// Wire method: `skills.applyUninstall`. /// /// # Parameters /// - /// * `params` - Session IDs to test for live in-use locks. + /// * `params` - One-use application of the exact retained Skill removal plan. /// /// # Returns /// - /// Session IDs from the input set that are currently in use by another process. + /// Skill installation result with the honoured contract, or a typed request/negotiation refusal. /// ///
/// @@ -3963,29 +6671,29 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn check_in_use( + pub async fn apply_uninstall( &self, - params: SessionsCheckInUseRequest, - ) -> Result { + params: SkillApplyUninstallRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_CHECKINUSE, Some(wire_params)) + .call(rpc_methods::SKILLS_APPLYUNINSTALL, 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. + /// Discovers skills across global and project sources. /// - /// Wire method: `sessions.getPersistedRemoteSteerable`. + /// Wire method: `skills.discover`. /// /// # Parameters /// - /// * `params` - Session ID to look up the persisted remote-steerable flag for. + /// * `params` - Optional project paths and additional skill directories to include in discovery. /// /// # Returns /// - /// The session's persisted remote-steerable flag, or omitted when no value has been persisted. + /// Skills discovered across global and project sources. /// ///
/// @@ -3994,32 +6702,26 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub(crate) async fn get_persisted_remote_steerable( - &self, - params: SessionsGetPersistedRemoteSteerableRequest, - ) -> Result { + pub async fn discover(&self, params: SkillsDiscoverRequest) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call( - rpc_methods::SESSIONS_GETPERSISTEDREMOTESTEERABLE, - Some(wire_params), - ) + .call(rpc_methods::SKILLS_DISCOVER, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Closes a session: emits shutdown, flushes pending events, releases the in-use lock, and disposes the active session. + /// 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. /// - /// Wire method: `sessions.close`. + /// Wire method: `skills.getDiscoveryPaths`. /// /// # Parameters /// - /// * `params` - Session ID to close. + /// * `params` - Optional project paths to enumerate. /// /// # Returns /// - /// 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. + /// Canonical locations where skills can be created so the runtime will recognize them. /// ///
/// @@ -4028,26 +6730,29 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn close(&self, params: SessionsCloseRequest) -> Result { + pub async fn get_discovery_paths( + &self, + params: SkillsGetDiscoveryPathsRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_CLOSE, Some(wire_params)) + .call(rpc_methods::SKILLS_GETDISCOVERYPATHS, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Closes, deactivates, and deletes a set of sessions, returning the bytes freed per session. + /// 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. /// - /// Wire method: `sessions.bulkDelete`. + /// Wire method: `skills.getDiscoveryPaths`. /// /// # Parameters /// - /// * `params` - Session IDs to close, deactivate, and delete from disk. + /// * `params` - Optional project paths to enumerate. /// /// # Returns /// - /// Map of sessionId -> bytes freed by removing the session's workspace directory. + /// Canonical locations where skills can be created so the runtime will recognize them. /// ///
/// @@ -4056,25 +6761,35 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn bulk_delete( + /// + /// Accepts [`SkillsGetDiscoveryPathsOptions`], including inputs added after [`SkillsGetDiscoveryPathsRequest`]. + pub async fn get_discovery_paths_with_options( &self, - params: SessionsBulkDeleteRequest, - ) -> Result { + params: SkillsGetDiscoveryPathsOptions, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_BULKDELETE, Some(wire_params)) + .call(rpc_methods::SKILLS_GETDISCOVERYPATHS, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } +} - /// Deletes one local session from disk after running the same lifecycle hooks as the session manager. +/// `skills.config.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcSkillsConfig<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcSkillsConfig<'a> { + /// Replaces the global list of disabled skills. /// - /// Wire method: `sessions.delete`. + /// Wire method: `skills.config.setDisabledSkills`. /// /// # Parameters /// - /// * `params` - Session ID to delete from disk. + /// * `params` - Skill names to mark as disabled in global configuration, replacing any previous list. /// ///
/// @@ -4083,26 +6798,28 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub(crate) async fn delete(&self, params: SessionsDeleteRequest) -> Result<(), Error> { + pub async fn set_disabled_skills( + &self, + params: SkillsConfigSetDisabledSkillsRequest, + ) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_DELETE, Some(wire_params)) + .call( + rpc_methods::SKILLS_CONFIG_SETDISABLEDSKILLS, + Some(wire_params), + ) .await?; Ok(()) } - /// Deletes sessions older than the given threshold, with optional dry-run and exclusion list. + /// Atomically adds or removes one skill from the disabled list. /// - /// Wire method: `sessions.pruneOld`. + /// Wire method: `skills.config.setSkillDisabled`. /// /// # Parameters /// - /// * `params` - Age threshold and optional flags controlling which old sessions are pruned (or simulated when dryRun is true). - /// - /// # Returns - /// - /// Outcome of the prune operation: deleted IDs, dry-run candidates, skipped IDs, total bytes freed, and the dry-run flag. + /// * `params` - Adds or removes a single skill from the global disabled list, leaving every other entry untouched. /// ///
/// @@ -4111,57 +6828,40 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn prune_old( + pub async fn set_skill_disabled( &self, - params: SessionsPruneOldRequest, - ) -> Result { + params: SkillsConfigSetSkillDisabledRequest, + ) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_PRUNEOLD, Some(wire_params)) + .call( + rpc_methods::SKILLS_CONFIG_SETSKILLDISABLED, + Some(wire_params), + ) .await?; - Ok(serde_json::from_value(_value)?) + Ok(()) } +} - /// Flushes a session's pending events to disk. - /// - /// Wire method: `sessions.save`. - /// - /// # Parameters - /// - /// * `params` - Session ID whose pending events should be flushed to disk. - /// - /// # Returns - /// - /// Flush a session's pending events to disk. No-op when no writer exists for the session (e.g., already closed). - /// - ///
- /// - /// **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. - /// - ///
- pub 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)?) - } +/// `skills.installations.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcSkillsInstallations<'a> { + pub(crate) client: &'a Client, +} - /// Releases the in-use lock held by this process for a session. +impl<'a> ClientRpcSkillsInstallations<'a> { + /// Lists owned verified Agent Finder Skill installations for the selected existing session. Listing is never gated by the Skill-install feature flag. /// - /// Wire method: `sessions.releaseLock`. + /// Wire method: `skills.installations.list`. /// /// # Parameters /// - /// * `params` - Session ID whose in-use lock should be released. + /// * `params` - Inventory request under an explicitly selected existing session. /// /// # Returns /// - /// 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. + /// Skill installation management result with the honoured contract, or a typed refusal. /// ///
/// @@ -4170,29 +6870,29 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn release_lock( + pub async fn list( &self, - params: SessionsReleaseLockRequest, - ) -> Result { + params: SkillInstallationsRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_RELEASELOCK, Some(wire_params)) + .call(rpc_methods::SKILLS_INSTALLATIONS_LIST, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Backfills missing summary and context fields on the supplied session metadata records. + /// Reconciles interrupted owned Skill installation work for the selected existing session, then inspects owned inventory. Recovery is never gated by the Skill-install feature flag. /// - /// Wire method: `sessions.enrichMetadata`. + /// Wire method: `skills.installations.recover`. /// /// # Parameters /// - /// * `params` - Session metadata records to enrich with summary and context information. + /// * `params` - Inventory request under an explicitly selected existing session. /// /// # Returns /// - /// The enriched metadata records, with summary and context fields backfilled where available. Sessions confirmed empty and unnamed are omitted. + /// Skill installation management result with the honoured contract, or a typed refusal. /// ///
/// @@ -4201,29 +6901,29 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn enrich_metadata( + pub async fn recover( &self, - params: SessionsEnrichMetadataRequest, - ) -> Result { + params: SkillInstallationsRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_ENRICHMETADATA, Some(wire_params)) + .call(rpc_methods::SKILLS_INSTALLATIONS_RECOVER, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Creates the workspace record for a session that has not been opened yet. A host that hands a session off to another application — writing the record and then launching that application against the session ID — needs the record on disk before any session exists to carry it, which the session-scoped workspace methods cannot do. Replaces any existing record and resets the checkpoint index. When writing to the local filesystem, a stored `fork_count` survives on disk. Returns the record it built, so a surviving stored `fork_count` can differ from the answer. + /// Inspects a known Skill installation operation on its original runtime connection. Status is never gated by the Skill-install feature flag. /// - /// Wire method: `sessions.createWorkspace`. + /// Wire method: `skills.installations.status`. /// /// # Parameters /// - /// * `params` - Identity, state location and starting context for a workspace record. + /// * `params` - Existing-operation control. A new session selector is deliberately not accepted. /// /// # Returns /// - /// The workspace record that was written. + /// Skill installation management result with the honoured contract, or a typed refusal. /// ///
/// @@ -4232,29 +6932,29 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub(crate) async fn create_workspace( + pub async fn status( &self, - params: SessionsCreateWorkspaceRequest, - ) -> Result { + params: SkillInstallationOperationRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_CREATEWORKSPACE, Some(wire_params)) + .call(rpc_methods::SKILLS_INSTALLATIONS_STATUS, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Reads a session's workspace record straight from disk, without opening the session. Resuming by session ID has to know where the session lives before it can connect, so the lookup cannot come from the session-scoped workspace methods, which resolve their location from a live session's context. Returns no record when the file is absent. + /// Requests cancellation of a known Skill installation operation before commit. Already-started durable work requires recovery instead of silent replay. /// - /// Wire method: `sessions.loadWorkspace`. + /// Wire method: `skills.installations.cancel`. /// /// # Parameters /// - /// * `params` - Where the session's state lives, as a root directory and the session ID under it. + /// * `params` - Existing-operation control. A new session selector is deliberately not accepted. /// /// # Returns /// - /// The workspace record on disk, omitted when the session has none. + /// Skill installation management result with the honoured contract, or a typed refusal. /// ///
/// @@ -4263,29 +6963,29 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub(crate) async fn load_workspace( + pub async fn cancel( &self, - params: SessionsLoadWorkspaceRequest, - ) -> Result { + params: SkillInstallationOperationRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_LOADWORKSPACE, Some(wire_params)) + .call(rpc_methods::SKILLS_INSTALLATIONS_CANCEL, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Merges fields into a session's workspace record on disk, creating the record when it is absent. The counterpart to `sessions.loadWorkspace`, for the same before-the-session-exists case. It preserves stored workspace-schema fields the request does not supply, does not preserve stored keys outside the workspace schema, and never replaces a stored `fork_count`. + /// Atomically persists enablement for one owned Agent Finder Skill and reconciles the selected bound session. Enablement is installation-scoped by receipt identity and is never gated by the Skill-install feature flag. /// - /// Wire method: `sessions.updateWorkspaceFields`. + /// Wire method: `skills.installations.setEnabled`. /// /// # Parameters /// - /// * `params` - Where the session's state lives, plus workspace-schema fields to merge into its workspace record. Stored keys outside the schema are not preserved, and a stored `fork_count` is never replaced. + /// * `params` - Persisted enablement update for one owned Skill installation. /// /// # Returns /// - /// The merge completed. The record carries the supplied workspace-schema fields, but a stored `fork_count` stays. + /// Skill installation management result with the honoured contract, or a typed refusal. /// ///
/// @@ -4294,32 +6994,40 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub(crate) async fn update_workspace_fields( + pub async fn set_enabled( &self, - params: SessionsUpdateWorkspaceFieldsRequest, - ) -> Result { + params: SkillSetEnabledRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client .call( - rpc_methods::SESSIONS_UPDATEWORKSPACEFIELDS, + rpc_methods::SKILLS_INSTALLATIONS_SETENABLED, Some(wire_params), ) .await?; Ok(serde_json::from_value(_value)?) } +} - /// Reloads user, plugin, and (optionally) repo hooks on the active session. +/// `tools.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcTools<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcTools<'a> { + /// Lists built-in tools available for a model. /// - /// Wire method: `sessions.reloadPluginHooks`. + /// Wire method: `tools.list`. /// /// # Parameters /// - /// * `params` - Active session ID and an optional flag for deferring repo-level hooks until folder trust. + /// * `params` - Optional model identifier whose tool overrides should be applied to the listing. /// /// # Returns /// - /// 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. + /// Built-in tools available for the requested model, with their parameters and instructions. /// ///
/// @@ -4328,29 +7036,49 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn reload_plugin_hooks( - &self, - params: SessionsReloadPluginHooksRequest, - ) -> Result { + pub async fn list(&self, params: ToolsListRequest) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_RELOADPLUGINHOOKS, Some(wire_params)) + .call(rpc_methods::TOOLS_LIST, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } +} - /// Loads previously-deferred repo-level hooks on the active session, returning queued startup prompts. +/// `user.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcUser<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcUser<'a> { + /// `user.settings.*` sub-namespace. + pub fn settings(&self) -> ClientRpcUserSettings<'a> { + ClientRpcUserSettings { + client: self.client, + } + } +} + +/// `user.settings.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcUserSettings<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcUserSettings<'a> { + /// Adds or removes one hook key in the user `disabledHooks` setting, rewriting settings.json in place and leaving every other setting untouched. Reports a typed outcome rather than failing the call, so a host can tell an unparseable settings file apart from a write that could not complete. /// - /// Wire method: `sessions.loadDeferredRepoHooks`. + /// Wire method: `user.settings.setHookDisabled`. /// /// # Parameters /// - /// * `params` - Active session ID whose deferred repo-level hooks should be loaded. + /// * `params` - One hook to disable or re-enable. /// /// # Returns /// - /// Queued repo-level startup prompts and the total hook command count after loading. + /// Outcome of rewriting the `disabledHooks` setting. /// ///
/// @@ -4359,32 +7087,32 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn load_deferred_repo_hooks( + pub(crate) async fn set_hook_disabled( &self, - params: SessionsLoadDeferredRepoHooksRequest, - ) -> Result { + params: UserSettingsSetHookDisabledRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client .call( - rpc_methods::SESSIONS_LOADDEFERREDREPOHOOKS, + rpc_methods::USER_SETTINGS_SETHOOKDISABLED, Some(wire_params), ) .await?; Ok(serde_json::from_value(_value)?) } - /// Replaces the manager-wide additional plugins registered with the session manager. + /// Reads the user's settings document exactly as `user.settings.load` does, but from a caller-supplied configuration directory instead of the one the server resolved for itself. Use this when a consumer scopes a session to its own Copilot home — the SDK's per-session `configDir` override — so the settings read match the directory that session actually uses. An absent or empty `configDir` resolves the server's own home, making this identical to `user.settings.load`. Hook-validation relaxations are deliberately not caller-supplied: they continue to come from the server's environment, so a consumer cannot widen the host's hook policy through this method. /// - /// Wire method: `sessions.setAdditionalPlugins`. + /// Wire method: `user.settings.loadForConfigDir`. /// /// # Parameters /// - /// * `params` - Manager-wide additional plugins to register; replaces any previously-configured set. + /// * `params` - Selects the configuration directory whose user settings to read. /// /// # Returns /// - /// 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. + /// The stored user settings document, keyed by setting name. /// ///
/// @@ -4393,32 +7121,32 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn set_additional_plugins( + pub(crate) async fn load_for_config_dir( &self, - params: SessionsSetAdditionalPluginsRequest, - ) -> Result { + params: UserSettingsLoadForConfigDirRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client .call( - rpc_methods::SESSIONS_SETADDITIONALPLUGINS, + rpc_methods::USER_SETTINGS_LOADFORCONFIGDIR, 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. + /// Reads a repository's checked-in settings document as stored, normalized, together with any diagnostic raised while reading it. This is the repository half of the layering `user.settings.get` reports: `user.settings.load` reads the user's own document, and this reads the one a repository checks in to govern the keys `user.settings.governanceKeys` lists as repo-governable. A document that fails to parse, or that sets a key a repository may not govern, is reported through `warning` while the settings that did load are still returned, so a consumer surfaces the diagnostic and continues rather than failing the session. When `repoRoot` is the user's home directory the Claude-compatible configuration is skipped, so a home-directory session does not inherit a personal config as repository policy. /// - /// Wire method: `sessions.getBoardEntryCount`. + /// Wire method: `user.settings.loadRepo`. /// /// # Parameters /// - /// * `params` - Session ID whose board entry count should be returned. + /// * `params` - Selects the repository whose checked-in settings to read. /// /// # Returns /// - /// Dynamic-context board entry count, when available. + /// A repository's settings as stored, together with any diagnostic raised while reading them. /// ///
/// @@ -4427,29 +7155,29 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub(crate) async fn get_board_entry_count( + pub(crate) async fn load_repo( &self, - params: SessionsGetBoardEntryCountRequest, - ) -> Result { + params: UserSettingsLoadRepoRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_GETBOARDENTRYCOUNT, Some(wire_params)) + .call(rpc_methods::USER_SETTINGS_LOADREPO, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// 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. + /// Reads a repository's settings and merges them over a supplied user layer in one step, returning the settings a consumer should act on inside that repository. Use this rather than reading the two layers separately: the merge is per setting and collection-aware, so a repository adds to the collections it is allowed to influence without replacing the user's own entries, and a whole-document overlay would get that wrong. Any diagnostic raised while reading the repository layer is returned rather than printed, so the consumer decides how to surface it; the settings that did load are merged regardless. /// - /// Wire method: `sessions.startRemoteControl`. + /// Wire method: `user.settings.loadAndMergeRepo`. /// /// # Parameters /// - /// * `params` - Parameters for attaching the remote-control singleton to a session. + /// * `params` - A repository to read settings from, and the user layer they are merged over. /// /// # Returns /// - /// Wrapper for the singleton's current status. + /// The settings a consumer should act on inside a repository, together with any diagnostic raised while reading the repository layer. /// ///
/// @@ -4458,29 +7186,28 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn start_remote_control( + pub(crate) async fn load_and_merge_repo( &self, - params: SessionsStartRemoteControlRequest, - ) -> Result { + params: UserSettingsLoadAndMergeRepoRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_STARTREMOTECONTROL, Some(wire_params)) + .call( + rpc_methods::USER_SETTINGS_LOADANDMERGEREPO, + 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`. - /// - /// # Parameters + /// Reports the on-disk path of the settings document `user.settings.load` and `user.settings.set` read and write. Resolved the same way those methods resolve it, so a host that shows or opens the settings file names the file the runtime actually uses, including the extensionless legacy `settings` file when that is what is present. /// - /// * `params` - Parameters for atomically rebinding the remote-control singleton. + /// Wire method: `user.settings.getPath`. /// /// # Returns /// - /// Outcome of a transferRemoteControl call. + /// The on-disk location of the user's settings document. /// ///
/// @@ -4489,32 +7216,22 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn transfer_remote_control( - &self, - params: SessionsTransferRemoteControlRequest, - ) -> Result { - let wire_params = serde_json::to_value(params)?; + pub(crate) async fn get_path(&self) -> Result { + let wire_params = serde_json::json!({}); let _value = self .client - .call( - rpc_methods::SESSIONS_TRANSFERREMOTECONTROL, - Some(wire_params), - ) + .call(rpc_methods::USER_SETTINGS_GETPATH, 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. + /// Reads the user's stored settings together with the diagnostic raised while reading them. This is `user.settings.load` plus the reason part of the document was ignored: that one answers the document alone, so a caller cannot tell a clean read from a salvaged one. Read this when the caller is about to write the file back, where accepting a partial parse would discard every value the loader could not read. The settings that did load are still reported alongside the warning, so a consumer may surface it and continue instead of failing. /// - /// Wire method: `sessions.setRemoteControlSteering`. - /// - /// # Parameters - /// - /// * `params` - Patch for the singleton's steering state. + /// Wire method: `user.settings.loadWithWarning`. /// /// # Returns /// - /// Wrapper for the singleton's current status. + /// The user's stored settings, together with any diagnostic raised while reading them. /// ///
/// @@ -4523,28 +7240,31 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn set_remote_control_steering( + pub(crate) async fn load_with_warning( &self, - params: SessionsSetRemoteControlSteeringRequest, - ) -> Result { - let wire_params = serde_json::to_value(params)?; + ) -> Result { + let wire_params = serde_json::json!({}); let _value = self .client .call( - rpc_methods::SESSIONS_SETREMOTECONTROLSTEERING, + rpc_methods::USER_SETTINGS_LOADWITHWARNING, 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). + /// Folds a user settings layer over a managed (enterprise policy) layer and returns the merged settings. The managed layer's governance keys win, `autoTier` resolves through its overridable form, and the two per-entry control maps (`enabledPlugins`, `extraKnownMarketplaces`) union across the layers and are then stamped back from the managed layer, so each entry an administrator declares wins while the user keeps control of the rest. The parameters are named for their layers rather than being a generic base/override pair: nothing about a settings object identifies which layer produced it, so the runtime cannot detect a transposed call, and transposing one resolves the governance keys in the user's favour instead of the administrator's. Supplying only one layer returns it unchanged, and supplying neither returns an empty object. Rejects a managed `autoTier` that is neither a user-configurable tier nor a single-key overridable form, rather than silently dropping it. /// - /// Wire method: `sessions.stopRemoteControl`. + /// Wire method: `user.settings.mergeManagedAndUser`. + /// + /// # Parameters + /// + /// * `params` - The managed (enterprise policy) layer and the user layer to fold together. The field names carry the layer identity: nothing about a settings object identifies which layer produced it, so a generic base/override pair would let a caller transpose them and silently resolve the governance keys in the user's favour. /// /// # Returns /// - /// Outcome of a stopRemoteControl call. + /// The user layer folded over the managed layer. /// ///
/// @@ -4553,26 +7273,32 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn stop_remote_control(&self) -> Result { - let wire_params = serde_json::json!({}); + pub(crate) async fn merge_managed_and_user( + &self, + params: UserSettingsMergeManagedAndUserParams, + ) -> Result { + let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_STOPREMOTECONTROL, Some(wire_params)) + .call( + rpc_methods::USER_SETTINGS_MERGEMANAGEDANDUSER, + 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). + /// Folds a repository's settings layer into the user layer. Each repo-overridable key carries a directional policy chosen so a repository can only add restrictions and never relax the user's own. The two per-entry managed control maps are the exception those directional rules cannot express — a repository may legitimately enable plugins and declare marketplaces — so they are re-stamped afterwards from the runtime's ambient managed policy. That makes the result depend on the runtime process's managed state and not on the arguments alone: a host must call this rather than reimplement the fold, or a repository could relax a restriction the administrator imposed. Rejects when either layer is not a JSON object. /// - /// Wire method: `sessions.stopRemoteControl`. + /// Wire method: `user.settings.mergeRepoValue`. /// /// # Parameters /// - /// * `params` - Parameters for stopping the remote-control singleton. + /// * `params` - The user layer and the repository layer to fold together. /// /// # Returns /// - /// Outcome of a stopRemoteControl call. + /// The repository layer folded into the user layer. /// ///
/// @@ -4581,25 +7307,29 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn stop_remote_control_with_params( + pub(crate) async fn merge_repo_value( &self, - params: SessionsStopRemoteControlRequest, - ) -> Result { + params: UserSettingsMergeRepoValueParams, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_STOPREMOTECONTROL, Some(wire_params)) + .call(rpc_methods::USER_SETTINGS_MERGEREPOVALUE, 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. + /// Runs the one-time migration that moves a staff account's update channel to stable, and records that it ran. Idempotent and self-gating: it takes a lock over the global-state file, re-reads the completion stamp inside it, and returns `current` rather than migrating twice, so two hosts racing at startup cannot both migrate. Only a `migrated` status means the persisted settings changed and the caller should reload them. An account on the experimental channel is deliberately left alone and reported as `excluded-experimental`. /// - /// Wire method: `sessions.getRemoteControlStatus`. + /// Wire method: `user.settings.migrateStaffUpdateChannelOnce`. + /// + /// # Parameters + /// + /// * `params` - The filesystem and environment facts a user-settings file is resolved from. Passed explicitly rather than read from the runtime's own process, so a host whose configuration directory differs from its process environment resolves the file it means. /// /// # Returns /// - /// Wrapper for the singleton's current status. + /// The outcome of the one-time staff update-channel migration. /// ///
/// @@ -4608,25 +7338,32 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn get_remote_control_status(&self) -> Result { - let wire_params = serde_json::json!({}); + pub(crate) async fn migrate_staff_update_channel_once( + &self, + params: UserSettingsHostContext, + ) -> Result { + let wire_params = serde_json::to_value(params)?; let _value = self .client .call( - rpc_methods::SESSIONS_GETREMOTECONTROLSTATUS, + rpc_methods::USER_SETTINGS_MIGRATESTAFFUPDATECHANNELONCE, Some(wire_params), ) .await?; Ok(serde_json::from_value(_value)?) } - /// Attaches (or detaches) an in-process ExtensionController delegate for the given session in a local host adapter. Pass `controller: undefined` to detach. Internal because the controller cannot cross the JSON-RPC boundary; the runtime manages its own session extension service. + /// Runs the one-time reset that clears a staff account's pinned model selection, and reports the timestamp the host should record. Idempotent and self-gating: the reset is gated on a fixed cutoff, so a caller that passes back the timestamp it recorded gets `didReset: false` rather than a second reset. It applies to staff accounts only; any other account is reported as not reset without the settings document being opened. Only `didReset: true` means the persisted settings changed, and only then are the updated settings returned for the host to adopt. /// - /// Wire method: `sessions.configureSessionExtensions`. + /// Wire method: `user.settings.resetStaffModelSelectionOnce`. /// /// # Parameters /// - /// * `params` - Params to attach or detach an in-process ExtensionController delegate. + /// * `params` - Selects the configuration directory to run the one-time staff model-selection reset against, and supplies the caller's record of whether it has already run. + /// + /// # Returns + /// + /// The outcome of the one-time staff model-selection reset. /// ///
/// @@ -4635,54 +7372,32 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub(crate) async fn configure_session_extensions( + pub(crate) async fn reset_staff_model_selection_once( &self, - params: ConfigureSessionExtensionsParams, - ) -> Result<(), Error> { + params: UserSettingsResetStaffModelSelectionOnceRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client .call( - rpc_methods::SESSIONS_CONFIGURESESSIONEXTENSIONS, + rpc_methods::USER_SETTINGS_RESETSTAFFMODELSELECTIONONCE, Some(wire_params), ) .await?; - Ok(()) - } -} - -/// `skills.*` RPCs. -#[derive(Clone, Copy)] -pub struct ClientRpcSkills<'a> { - pub(crate) client: &'a Client, -} - -impl<'a> ClientRpcSkills<'a> { - /// `skills.config.*` sub-namespace. - pub fn config(&self) -> ClientRpcSkillsConfig<'a> { - ClientRpcSkillsConfig { - client: self.client, - } - } - - /// `skills.installations.*` sub-namespace. - pub fn installations(&self) -> ClientRpcSkillsInstallations<'a> { - ClientRpcSkillsInstallations { - client: self.client, - } + Ok(serde_json::from_value(_value)?) } - /// Plans installation of a verified Agent Finder Skill candidate without writing files. The returned review is safe to present to a user and installing always leaves the Skill disabled until separately enabled. + /// Reports the top-level keys in the user settings document that the runtime does not recognize, phrased for display to the user. This is a separate question from the warning `user.settings.loadWithWarning` returns: that one describes parts of the document the loader had to ignore to load the rest, whereas an unknown key parses cleanly and is simply not a setting, so a host that surfaces only the load warning silently accepts a misspelled key that will never take effect. Nothing is reported when the document is absent, unreadable, or failed to load, because an unknown-key list derived from a document the runtime could not read would be misleading rather than helpful. Inspection never modifies the document. /// - /// Wire method: `skills.planInstall`. + /// Wire method: `user.settings.getUnknownKeysWarning`. /// /// # Parameters /// - /// * `params` - Side-effect-free planning of one verified Agent Finder Skill candidate. + /// * `params` - Selects the configuration directory whose user settings document should be inspected for unrecognized top-level keys. /// /// # Returns /// - /// Skill installation management result with the honoured contract, or a typed refusal. + /// The unrecognized top-level keys found in the user settings document, already phrased for display. /// ///
/// @@ -4691,29 +7406,32 @@ impl<'a> ClientRpcSkills<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn plan_install( + pub(crate) async fn get_unknown_keys_warning( &self, - params: SkillPlanInstallRequest, - ) -> Result { + params: UserSettingsUnknownKeysWarningRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SKILLS_PLANINSTALL, Some(wire_params)) + .call( + rpc_methods::USER_SETTINGS_GETUNKNOWNKEYSWARNING, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } - /// Consumes one verified Skill installation plan, requests explicit human consent through installations.confirm on the original connection, then revalidates and installs the Skill disabled. + /// Resolves the effective runtime settings for a host that is about to start a session, layering the given overrides over the settings documents, the environment and the platform defaults. A host cannot assemble this itself: the precedence between a user document, a repository document, the managed layers and the environment is the runtime's own rule, and a host that re-implements it drifts from the server it is configuring. The working directory, environment and platform are taken as arguments rather than read from the server process, so a host resolving settings for a directory other than its own gets that directory's answer. Nothing is written; this only computes. /// - /// Wire method: `skills.applyInstall`. + /// Wire method: `user.settings.buildRuntime`. /// /// # Parameters /// - /// * `params` - Applies exactly one retained verified Skill installation plan. + /// * `params` - The overrides and host context to resolve effective runtime settings from. /// /// # Returns /// - /// Skill installation result with the honoured contract, or a typed request/negotiation refusal. + /// The effective runtime settings, keyed by setting name. Keys this runtime does not recognize are preserved rather than dropped, so a caller reading a document written by a newer runtime keeps every value. /// ///
/// @@ -4722,29 +7440,29 @@ impl<'a> ClientRpcSkills<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn apply_install( + pub(crate) async fn build_runtime( &self, - params: SkillApplyInstallRequest, - ) -> Result { + params: UserSettingsBuildRuntimeRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SKILLS_APPLYINSTALL, Some(wire_params)) + .call(rpc_methods::USER_SETTINGS_BUILDRUNTIME, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Prepares a read-only removal plan for an owned verified Agent Finder Skill installation. Uninstall planning is never gated by the Skill-install feature flag. + /// Runs the one-time migration that moves a staff account's log level to the current default, and records that it ran. Idempotent and self-gating: it takes a lock over the global-state file and re-reads the completion stamp inside it, so two hosts racing at startup cannot both migrate. Only `refreshed` can have changed the persisted settings, and the settings it returns are the document re-read after the write, so a host should adopt them rather than reloading separately and racing the next writer. /// - /// Wire method: `skills.planUninstall`. + /// Wire method: `user.settings.migrateStaffLogLevelOnce`. /// /// # Parameters /// - /// * `params` - Read-only preparation of one owned Skill removal under fresh selected-session authority. + /// * `params` - Selects the configuration directory to run the one-time staff log-level migration against, and supplies the caller's record of whether it has already run. /// /// # Returns /// - /// Skill installation management result with the honoured contract, or a typed refusal. + /// The outcome of the one-time staff log-level migration. /// ///
/// @@ -4753,29 +7471,32 @@ impl<'a> ClientRpcSkills<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn plan_uninstall( + pub(crate) async fn migrate_staff_log_level_once( &self, - params: SkillPlanUninstallRequest, - ) -> Result { + params: UserSettingsMigrateStaffLogLevelOnceRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SKILLS_PLANUNINSTALL, Some(wire_params)) + .call( + rpc_methods::USER_SETTINGS_MIGRATESTAFFLOGLEVELONCE, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } - /// Consumes an owned Skill removal plan, requests explicit human consent through installations.confirm, refuses drift, and removes the exact owned files through quarantine. + /// Runs the one-time cleanup that removes obsolete CLI-owned settings and legacy rubber-duck keys from the user's settings document, and reports what it did. Idempotent and safe to call on every startup: with nothing obsolete left it reports `unchanged` and writes nothing. The cleanup preserves every key it does not recognize, so a setting written by a newer host is never dropped by an older one, and a document that fails to parse is reported `invalid` and left untouched rather than rewritten from a partial read. The cleaned document is not returned: it is written before this replies, so `user.settings.load` reads it authoritatively. A host that surfaces unrecognized keys through `user.settings.unknownKeys` wants this first, because the keys this retires would otherwise be shown to the user as if they were misspelled settings. /// - /// Wire method: `skills.applyUninstall`. + /// Wire method: `user.settings.migrateObsoleteOnDisk`. /// /// # Parameters /// - /// * `params` - One-use application of the exact retained Skill removal plan. + /// * `params` - Selects the configuration directory whose settings document to clean. /// /// # Returns /// - /// Skill installation result with the honoured contract, or a typed request/negotiation refusal. + /// The outcome of the one-time obsolete-settings cleanup. /// ///
/// @@ -4784,29 +7505,28 @@ impl<'a> ClientRpcSkills<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn apply_uninstall( + pub(crate) async fn migrate_obsolete_on_disk( &self, - params: SkillApplyUninstallRequest, - ) -> Result { + params: UserSettingsMigrateObsoleteOnDiskRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SKILLS_APPLYUNINSTALL, Some(wire_params)) + .call( + rpc_methods::USER_SETTINGS_MIGRATEOBSOLETEONDISK, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } - /// Discovers skills across global and project sources. - /// - /// Wire method: `skills.discover`. - /// - /// # Parameters + /// Lists every known user setting from settings.json, each with its effective value, its default, and whether it is at the default — so settings the user has never set still appear with their default value. Does not include repository- or enterprise-managed overrides that the runtime layers on top at session time. /// - /// * `params` - Optional project paths and additional skill directories to include in discovery. + /// Wire method: `user.settings.get`. /// /// # Returns /// - /// Skills discovered across global and project sources. + /// Per-key metadata for every known user setting in settings.json, including settings left at their default. Excludes repository- and enterprise-managed overrides. /// ///
/// @@ -4815,26 +7535,26 @@ impl<'a> ClientRpcSkills<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn discover(&self, params: SkillsDiscoverRequest) -> Result { - let wire_params = serde_json::to_value(params)?; + pub async fn get(&self) -> Result { + let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::SKILLS_DISCOVER, Some(wire_params)) + .call(rpc_methods::USER_SETTINGS_GET, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// 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. + /// Resolves the on-disk locations of the settings files this runtime reads, so a consumer can show or open them. The user path is the same file `user.settings.get`, `user.settings.load`, and `user.settings.set` operate on. Supply `repoRoot` to also resolve that repository's checked-in and local settings paths. A path is reported whether or not the file exists. /// - /// Wire method: `skills.getDiscoveryPaths`. + /// Wire method: `user.settings.getPaths`. /// /// # Parameters /// - /// * `params` - Optional project paths to enumerate. + /// * `params` - Selects which settings file paths to resolve. /// /// # Returns /// - /// Canonical locations where skills can be created so the runtime will recognize them. + /// On-disk locations of the settings files this runtime reads. A path is reported whether or not the file exists. /// ///
/// @@ -4843,33 +7563,25 @@ impl<'a> ClientRpcSkills<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn get_discovery_paths( + pub(crate) async fn get_paths( &self, - params: SkillsGetDiscoveryPathsRequest, - ) -> Result { + params: UserSettingsGetPathsRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SKILLS_GETDISCOVERYPATHS, Some(wire_params)) + .call(rpc_methods::USER_SETTINGS_GETPATHS, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } -} - -/// `skills.config.*` RPCs. -#[derive(Clone, Copy)] -pub struct ClientRpcSkillsConfig<'a> { - pub(crate) client: &'a Client, -} -impl<'a> ClientRpcSkillsConfig<'a> { - /// Replaces the global list of disabled skills. + /// Writes one or more user settings to settings.json, replacing each provided top-level key. A key whose value is null is removed. /// - /// Wire method: `skills.config.setDisabledSkills`. + /// Wire method: `user.settings.set`. /// /// # Parameters /// - /// * `params` - Skill names to mark as disabled in global configuration, replacing any previous list. + /// * `params` - Partial user settings to write to settings.json. Each top-level key is written individually, replacing the existing value; a key whose value is null is removed. /// ///
/// @@ -4878,28 +7590,22 @@ impl<'a> ClientRpcSkillsConfig<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn set_disabled_skills( - &self, - params: SkillsConfigSetDisabledSkillsRequest, - ) -> Result<(), Error> { + pub async fn set(&self, params: UserSettingsSetRequest) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call( - rpc_methods::SKILLS_CONFIG_SETDISABLEDSKILLS, - Some(wire_params), - ) + .call(rpc_methods::USER_SETTINGS_SET, Some(wire_params)) .await?; Ok(()) } - /// Atomically adds or removes one skill from the disabled list. + /// Lists the top-level user-setting keys by who is allowed to govern them: `repo` keys a repository may override through `.github/copilot/settings.json`, `claudeRepo` keys a repository may override through a Claude-compatible config, and `managed` keys an enterprise policy may fix. The lists are static for a given runtime build and fail closed: a key absent from a list cannot be governed that way. /// - /// Wire method: `skills.config.setSkillDisabled`. + /// Wire method: `user.settings.listGovernanceKeys`. /// - /// # Parameters + /// # Returns /// - /// * `params` - Adds or removes a single skill from the global disabled list, leaving every other entry untouched. + /// Top-level user-setting keys grouped by who may govern them. /// ///
/// @@ -4908,40 +7614,31 @@ impl<'a> ClientRpcSkillsConfig<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn set_skill_disabled( + pub(crate) async fn list_governance_keys( &self, - params: SkillsConfigSetSkillDisabledRequest, - ) -> Result<(), Error> { - let wire_params = serde_json::to_value(params)?; + ) -> Result { + let wire_params = serde_json::json!({}); let _value = self .client .call( - rpc_methods::SKILLS_CONFIG_SETSKILLDISABLED, + rpc_methods::USER_SETTINGS_LISTGOVERNANCEKEYS, Some(wire_params), ) .await?; - Ok(()) + Ok(serde_json::from_value(_value)?) } -} - -/// `skills.installations.*` RPCs. -#[derive(Clone, Copy)] -pub struct ClientRpcSkillsInstallations<'a> { - pub(crate) client: &'a Client, -} -impl<'a> ClientRpcSkillsInstallations<'a> { - /// Lists owned verified Agent Finder Skill installations for the selected existing session. Listing is never gated by the Skill-install feature flag. + /// Atomically edits one canonical user setting inside its settings-file lock. Reads, canonicalizes touched aliases, applies the edit, validates the resulting top-level setting and writes before releasing the lock. An explicit autoUpdatesChannel set completes the staff migration in the same transaction. Only a malformed existing file returns `invalid`; refused keys, invalid values and I/O errors reject the call. /// - /// Wire method: `skills.installations.list`. + /// Wire method: `user.settings.edit`. /// /// # Parameters /// - /// * `params` - Inventory request under an explicitly selected existing session. + /// * `params` - One atomic edit of a canonical setting in settings.json. /// /// # Returns /// - /// Skill installation management result with the honoured contract, or a typed refusal. + /// Outcome of an atomic user or repository settings edit. /// ///
/// @@ -4950,29 +7647,29 @@ impl<'a> ClientRpcSkillsInstallations<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn list( + pub(crate) async fn edit( &self, - params: SkillInstallationsRequest, - ) -> Result { + params: UserSettingsEditRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SKILLS_INSTALLATIONS_LIST, Some(wire_params)) + .call(rpc_methods::USER_SETTINGS_EDIT, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Reconciles interrupted owned Skill installation work for the selected existing session, then inspects owned inventory. Recovery is never gated by the Skill-install feature flag. + /// Rewrites legacy and snake_case setting names in a settings object to their canonical spelling. A caller editing settings.json must normalize before writing: a legacy key left in place survives the write, is re-normalized on the next load, and silently shadows the canonical value the write just stored. /// - /// Wire method: `skills.installations.recover`. + /// Wire method: `user.settings.normalize`. /// /// # Parameters /// - /// * `params` - Inventory request under an explicitly selected existing session. + /// * `params` - Settings object whose key names should be canonicalized. /// /// # Returns /// - /// Skill installation management result with the honoured contract, or a typed refusal. + /// Settings object with canonical key names. /// ///
/// @@ -4981,29 +7678,29 @@ impl<'a> ClientRpcSkillsInstallations<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn recover( + pub(crate) async fn normalize( &self, - params: SkillInstallationsRequest, - ) -> Result { + params: UserSettingsNormalizeRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SKILLS_INSTALLATIONS_RECOVER, Some(wire_params)) + .call(rpc_methods::USER_SETTINGS_NORMALIZE, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Inspects a known Skill installation operation on its original runtime connection. Status is never gated by the Skill-install feature flag. + /// Diagnoses a set of already-loaded settings layers, reporting each key that will not take effect and why. Settings load tolerantly so that one bad key does not stop a session from starting; this method surfaces what was dropped so a caller can report it instead of silently ignoring the user's intent. Takes the layers as given rather than reading them, so a caller can diagnose exactly the content it is showing, including unsaved edits. Never echoes a setting's value, because a value may be a token or another secret. Marked internal because it exists to carry a CLI call site off the native addon, not to serve a consumer-facing need; it becomes public once an SDK consumer needs to inspect settings health itself. /// - /// Wire method: `skills.installations.status`. + /// Wire method: `user.settings.diagnoseProblems`. /// /// # Parameters /// - /// * `params` - Existing-operation control. A new session selector is deliberately not accepted. + /// * `params` - The settings layers to diagnose. /// /// # Returns /// - /// Skill installation management result with the honoured contract, or a typed refusal. + /// The settings that will not take effect across the given layers. /// ///
/// @@ -5012,29 +7709,32 @@ impl<'a> ClientRpcSkillsInstallations<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn status( + pub(crate) async fn diagnose_problems( &self, - params: SkillInstallationOperationRequest, - ) -> Result { + params: UserSettingsProblemsRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SKILLS_INSTALLATIONS_STATUS, Some(wire_params)) + .call( + rpc_methods::USER_SETTINGS_DIAGNOSEPROBLEMS, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } - /// Requests cancellation of a known Skill installation operation before commit. Already-started durable work requires recovery instead of silent replay. + /// Validates one top-level setting subtree and writes just that key to settings.json as a single step. Doing both here closes the race a caller cannot close itself: between a separate preflight read and the write, the file may change. Rewriting only the touched key keeps legacy siblings out of settings.json. Answers a status rather than failing, so a validation problem and a write problem stay distinguishable. /// - /// Wire method: `skills.installations.cancel`. + /// Wire method: `user.settings.applyTopKey`. /// /// # Parameters /// - /// * `params` - Existing-operation control. A new session selector is deliberately not accepted. + /// * `params` - A single top-level setting subtree to validate and write. /// /// # Returns /// - /// Skill installation management result with the honoured contract, or a typed refusal. + /// Outcome of validating and writing one top-level setting subtree. /// ///
/// @@ -5043,29 +7743,25 @@ impl<'a> ClientRpcSkillsInstallations<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn cancel( + pub(crate) async fn apply_top_key( &self, - params: SkillInstallationOperationRequest, - ) -> Result { + params: UserSettingsApplyTopKeyRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SKILLS_INSTALLATIONS_CANCEL, Some(wire_params)) + .call(rpc_methods::USER_SETTINGS_APPLYTOPKEY, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Atomically persists enablement for one owned Agent Finder Skill and reconciles the selected bound session. Enablement is installation-scoped by receipt identity and is never gated by the Skill-install feature flag. + /// Replaces the user settings file's entire content in one write. A key absent from `settings` is removed, which is what distinguishes this from `user.settings.set`: that one merges, so it can never clear a key the caller has dropped. For a caller saving the result of a raw edit, a merge would silently resurrect every key the user deleted. Records the same one-time mutation marks a per-key write records, so a setting the user has now chosen explicitly stops being migrated for them. /// - /// Wire method: `skills.installations.setEnabled`. + /// Wire method: `user.settings.writeAll`. /// /// # Parameters /// - /// * `params` - Persisted enablement update for one owned Skill installation. - /// - /// # Returns - /// - /// Skill installation management result with the honoured contract, or a typed refusal. + /// * `params` - The whole content to store in the user settings file. /// ///
/// @@ -5074,40 +7770,26 @@ impl<'a> ClientRpcSkillsInstallations<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn set_enabled( - &self, - params: SkillSetEnabledRequest, - ) -> Result { + pub(crate) async fn write_all(&self, params: UserSettingsWriteAllRequest) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call( - rpc_methods::SKILLS_INSTALLATIONS_SETENABLED, - Some(wire_params), - ) + .call(rpc_methods::USER_SETTINGS_WRITEALL, Some(wire_params)) .await?; - Ok(serde_json::from_value(_value)?) + Ok(()) } -} - -/// `tools.*` RPCs. -#[derive(Clone, Copy)] -pub struct ClientRpcTools<'a> { - pub(crate) client: &'a Client, -} -impl<'a> ClientRpcTools<'a> { - /// Lists built-in tools available for a model. + /// Merges a partial object into one top-level user setting, re-reading the stored value inside this runtime process's settings.json write lock so that concurrent writers in this process to different sub-fields of the same key do not clobber each other. Unlike `user.settings.set`, which replaces a key's whole value with what the caller last read, this keeps sub-fields the caller did not name when both the stored value and supplied value are objects; otherwise the supplied value replaces the stored value. Use it whenever two independent surfaces own different sub-fields of one setting. Returns the key's stored value after the write, in `value`. /// - /// Wire method: `tools.list`. + /// Wire method: `user.settings.update`. /// /// # Parameters /// - /// * `params` - Optional model identifier whose tool overrides should be applied to the listing. + /// * `params` - Names the user setting to merge into, and the partial object to merge. /// /// # Returns /// - /// Built-in tools available for the requested model, with their parameters and instructions. + /// The stored value of the user setting after the merge. /// ///
/// @@ -5116,45 +7798,25 @@ impl<'a> ClientRpcTools<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn list(&self, params: ToolsListRequest) -> Result { + pub(crate) async fn update( + &self, + params: UserSettingsUpdateRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::TOOLS_LIST, Some(wire_params)) + .call(rpc_methods::USER_SETTINGS_UPDATE, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } -} - -/// `user.*` RPCs. -#[derive(Clone, Copy)] -pub struct ClientRpcUser<'a> { - pub(crate) client: &'a Client, -} - -impl<'a> ClientRpcUser<'a> { - /// `user.settings.*` sub-namespace. - pub fn settings(&self) -> ClientRpcUserSettings<'a> { - ClientRpcUserSettings { - client: self.client, - } - } -} -/// `user.settings.*` RPCs. -#[derive(Clone, Copy)] -pub struct ClientRpcUserSettings<'a> { - pub(crate) client: &'a Client, -} - -impl<'a> ClientRpcUserSettings<'a> { - /// Lists every known user setting from settings.json, each with its effective value, its default, and whether it is at the default — so settings the user has never set still appear with their default value. Does not include repository- or enterprise-managed overrides that the runtime layers on top at session time. + /// Reads settings.json as stored for a caller that is about to edit it. Unlike `user.settings.get`, this returns file values rather than per-key metadata and defaults. Reports `missing` for an absent file and `invalid` when the existing file will not parse. Retired config.json and extensionless files are not read. /// - /// Wire method: `user.settings.get`. + /// Wire method: `user.settings.loadForEdit`. /// /// # Returns /// - /// Per-key metadata for every known user setting in settings.json, including settings left at their default. Excludes repository- and enterprise-managed overrides. + /// Contents of settings.json as stored, for a caller about to rewrite it. /// ///
/// @@ -5163,22 +7825,26 @@ impl<'a> ClientRpcUserSettings<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn get(&self) -> Result { + pub(crate) async fn load_for_edit(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::USER_SETTINGS_GET, Some(wire_params)) + .call(rpc_methods::USER_SETTINGS_LOADFOREDIT, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Writes one or more user settings to settings.json, replacing each provided top-level key. A key whose value is null is removed. + /// Validates one top-level setting subtree without writing it, reporting each problem with the path it applies to. Scoped to a single top-level key on purpose: validating the whole file would reject an edit whenever an unrelated pre-existing field is malformed, locking the user out of the very tool that could fix it. /// - /// Wire method: `user.settings.set`. + /// Wire method: `user.settings.validate`. /// /// # Parameters /// - /// * `params` - Partial user settings to write to settings.json. Each top-level key is written individually, replacing the existing value; a key whose value is null is removed. + /// * `params` - A single top-level setting subtree to validate. + /// + /// # Returns + /// + /// Outcome of validating one top-level setting subtree. /// ///
/// @@ -5187,13 +7853,16 @@ impl<'a> ClientRpcUserSettings<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn set(&self, params: UserSettingsSetRequest) -> Result<(), Error> { + pub(crate) async fn validate( + &self, + params: UserSettingsValidateRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::USER_SETTINGS_SET, Some(wire_params)) + .call(rpc_methods::USER_SETTINGS_VALIDATE, Some(wire_params)) .await?; - Ok(()) + Ok(serde_json::from_value(_value)?) } } @@ -10824,6 +13493,42 @@ impl<'a> SessionRpcModel<'a> { Ok(serde_json::from_value(_value)?) } + /// Computes whether the session's current model shows reasoning summaries and how a host should present them: the default Ctrl+T visibility, reasoning headers, the hidden-summaries hint, and whether a persisted `showReasoning` setting is redundant. The runtime folds in the session's model catalog, selection and reasoning-summaries experiment assignment; the host supplies only its own persisted setting and current visibility. This method reads the session's resolved model catalog rather than resolving it, so call `session.model.list` first on a session that has not listed models or run a turn; until then the result is `ready: false`. A session that resolves models without a Copilot catalog, that is a BYOK registry or an alternate provider, never caches one and stays `ready: false`. + /// + /// Wire method: `session.model.getReasoningSummaries`. + /// + /// # Parameters + /// + /// * `params` - Host presentation state the reasoning-summaries policy depends on. + /// + /// # Returns + /// + /// Reasoning-summaries presentation policy for the session's current model. + /// + ///
+ /// + /// **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. + /// + ///
+ pub async fn get_reasoning_summaries( + &self, + params: ModelGetReasoningSummariesRequest, + ) -> 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_GETREASONINGSUMMARIES, + 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`. @@ -12168,6 +14873,64 @@ impl<'a> SessionRpcPlugins<'a> { .await?; Ok(()) } + + /// Installs or updates the plugins the session's managed settings require now, without waiting for the next automatic attempt after a failure, then reloads plugins. Reports progress and failures with the same `session.info` and `session.warning` events as automatic preparation. Fails when the runtime does not prepare managed plugins for the session. + /// + /// Wire method: `session.plugins.retryManaged`. + /// + /// # Returns + /// + /// Per-plugin outcomes of retrying the plugins required by the session's managed settings. + /// + ///
+ /// + /// **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. + /// + ///
+ pub async fn retry_managed(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_PLUGINS_RETRYMANAGED, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Installs or updates the plugins the session's managed settings require now, without waiting for the next automatic attempt after a failure, then reloads plugins. Reports progress and failures with the same `session.info` and `session.warning` events as automatic preparation. Fails when the runtime does not prepare managed plugins for the session. + /// + /// Wire method: `session.plugins.retryManaged`. + /// + /// # Parameters + /// + /// * `params` - Plugins required by the session's managed settings to install or update again. + /// + /// # Returns + /// + /// Per-plugin outcomes of retrying the plugins required by the session's managed settings. + /// + ///
+ /// + /// **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. + /// + ///
+ pub async fn retry_managed_with_params( + &self, + params: SessionPluginsRetryManagedRequest, + ) -> 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_PLUGINS_RETRYMANAGED, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.plugins.marketplaces.*` RPCs. @@ -13549,6 +16312,36 @@ impl<'a> SessionRpcSandbox<'a> { Ok(serde_json::from_value(_value)?) } + /// Lists possible secret-bearing environment variables available to new sandboxed shells that are not already covered by credential masking. Uses local name rules and known-provider host suggestions, never returns secret values, and changes no settings or network permissions. Returns no suggestions while the session is not sandboxed. + /// + /// Wire method: `session.sandbox.getCredentialSuggestions`. + /// + /// # Returns + /// + /// Possible unconfigured secrets in the sandbox shell environment, sorted by variable name, with no secret values. + /// + ///
+ /// + /// **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. + /// + ///
+ pub async fn get_credential_suggestions( + &self, + ) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_SANDBOX_GETCREDENTIALSUGGESTIONS, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Disables sandboxing for the remainder of the current session and approves the referenced pending sandbox-bypass permission request. The request is rejected unless the exact request is still pending and the effective sandbox policy permits bypass. /// /// Wire method: `session.sandbox.disableForSession`. diff --git a/rust/src/generated/session_events.rs b/rust/src/generated/session_events.rs index 4872f9ab0d..13b80797dd 100644 --- a/rust/src/generated/session_events.rs +++ b/rust/src/generated/session_events.rs @@ -1356,13 +1356,19 @@ pub struct SessionErrorData { /// Fine-grained error code from the upstream provider, when available. For `errorType: "rate_limit"`, this is one of the `RateLimitErrorCode` values (e.g., `"user_weekly_rate_limited"`, `"user_global_rate_limited"`, `"rate_limited"`, `"user_model_rate_limited"`, `"integration_rate_limited"`). For `errorType: "quota"`, this is the CAPI quota error code (e.g., `"quota_exceeded"`, `"session_quota_exceeded"`, `"billing_not_configured"`). #[serde(skip_serializing_if = "Option::is_none")] pub error_code: Option, - /// Category of error (e.g., "authentication", "authorization", "quota", "rate_limit", "context_limit", "query") + /// Category of error (e.g., "authentication", "authorization", "policy", "quota", "rate_limit", "context_limit", "query") pub error_type: String, /// Human-readable error message pub message: String, /// GitHub request tracing ID (x-github-request-id header) for correlating with server-side logs #[serde(skip_serializing_if = "Option::is_none")] pub provider_call_id: Option, + /// Sanitized provider-defined error parameter preserved from a structured upstream error envelope, when available. + #[serde(skip_serializing_if = "Option::is_none")] + pub provider_error_param: Option, + /// Provider-defined error type preserved from a structured upstream error envelope, when available (for example, `invalid_request_error`). + #[serde(skip_serializing_if = "Option::is_none")] + pub provider_error_type: Option, /// What the user must do to recover, when the runtime knows of an action. The `message` never names a client affordance, so a client that offers one — a slash command, a settings pane, a link — renders it from this value. #[serde(skip_serializing_if = "Option::is_none")] pub remediation: Option, @@ -2273,6 +2279,37 @@ pub struct ProviderQuotaBudgetMetadata { pub usage_allowed_when_exhausted: bool, } +/// An authoritative monthly usage reading for the observation's provider, account, and service. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ProviderMonthlyUsage { + /// Nonnegative finite consumption reported by the service, preserving zero and fractions. Present only when state is available; never computed from tokens or balance differences. + #[serde(skip_serializing_if = "Option::is_none")] + pub consumed_quantity: Option, + /// Start of the service-reported monthly billing cycle, in UTC RFC 3339. + #[serde(skip_serializing_if = "Option::is_none")] + pub cycle_start: Option, + /// Service read time in UTC RFC 3339, not a ledger reconciliation watermark or confirmation of the latest inference charge. + #[serde(skip_serializing_if = "Option::is_none")] + pub queried_at: Option, + /// End of the service-reported monthly billing cycle, in UTC RFC 3339. + #[serde(skip_serializing_if = "Option::is_none")] + pub reset_on: Option, + /// Identity scope of the reading within its provider and service. + pub scope: ProviderMonthlyUsageScope, + /// Availability of monthly consumption. Missing or unavailable usage must never be interpreted as zero. + pub state: ProviderMonthlyUsageState, + /// Unit of consumedQuantity; independent of the parent observation's balance unit. + pub unit: ProviderQuotaUnit, +} + /// An account quota reading. Absence of a quantity is unknown, never zero. /// ///
@@ -2312,6 +2349,9 @@ pub struct ProviderQuotaState { /// HTTP status from acquisition, when available. #[serde(skip_serializing_if = "Option::is_none")] pub http_status: Option, + /// Service-reported monthly consumption, independent of quota balances and per-call or session cost. Omitted when the service does not report monthly usage. + #[serde(skip_serializing_if = "Option::is_none")] + pub monthly_usage: Option, /// Whether this is a GET account reading or a pre-response admission observation. Observations are never merged across kinds. #[serde(skip_serializing_if = "Option::is_none")] pub observation_kind: Option, @@ -2349,7 +2389,7 @@ pub struct ProviderQuotaState { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SessionQuotaObservationData { - /// The admitted provider's state observation. Admission observations never contain quantities or reset/percentage semantics. + /// The admitted provider's state observation. Admission balances and percentages remain absent; separate monthly usage may report its own amount and cycle. pub observation: ProviderQuotaState, } @@ -2516,6 +2556,9 @@ pub struct CompactionCompleteCompactionTokensUsed { /// Provider captured for the compaction call; absent when attribution is unknown. #[serde(skip_serializing_if = "Option::is_none")] pub provider: Option, + /// Number of model requests represented by this aggregated compaction usage + #[serde(skip_serializing_if = "Option::is_none")] + pub request_count: Option, } /// Original request-level and effective conversation reasoning effort for a provider history boundary; the historical type name is retained for compatibility @@ -2643,6 +2686,12 @@ pub struct PermissionRecoveryData { pub max_attempts: i64, /// Policy selected from the current client's response capability; mode or client changes may update it during recovery pub on_blocked: PermissionRecoveryOnBlocked, + /// Subagent identifier that owns this recovery episode when the request was delegated through a parent permission service + #[serde(skip_serializing_if = "Option::is_none")] + pub owner_agent_id: Option, + /// Root or child execution-session identifier that owns this recovery episode; omitted for legacy snapshots + #[serde(skip_serializing_if = "Option::is_none")] + pub owner_session_id: Option, /// Controlled reason for the latest episode transition pub reason: PermissionRecoveryReason, /// Current lifecycle state of the recovery episode @@ -3076,6 +3125,12 @@ pub struct SessionPermissionRecoveryData { pub max_attempts: i64, /// Policy selected from the current client's response capability; mode or client changes may update it during recovery pub on_blocked: PermissionRecoveryOnBlocked, + /// Subagent identifier that owns this recovery episode when the request was delegated through a parent permission service + #[serde(skip_serializing_if = "Option::is_none")] + pub owner_agent_id: Option, + /// Root or child execution-session identifier that owns this recovery episode; omitted for legacy snapshots + #[serde(skip_serializing_if = "Option::is_none")] + pub owner_session_id: Option, /// Controlled reason for the latest episode transition pub reason: PermissionRecoveryReason, /// Current lifecycle state of the recovery episode @@ -4096,9 +4151,15 @@ pub struct AssistantUsageData { /// Number of rejected speculative prediction tokens #[serde(skip_serializing_if = "Option::is_none")] pub rejected_prediction_tokens: Option, - /// Serialized (uncompressed) byte length of the request body. A content-free size signal. + /// Serialized (uncompressed) byte length of the request body, before any Content-Encoding. A content-free size signal. #[serde(skip_serializing_if = "Option::is_none")] pub request_body_bytes: Option, + /// Content-Encoding applied to the request body of the attempt that produced this outcome; identity when uncompressed + #[serde(skip_serializing_if = "Option::is_none")] + pub request_body_encoding: Option, + /// Byte length of the request body actually sent on the wire for the attempt that produced this outcome, after any Content-Encoding. Equals requestBodyBytes when the body was not compressed. + #[serde(skip_serializing_if = "Option::is_none")] + pub request_body_wire_bytes: Option, /// Per-request treatment/eligibility signal returned by the Copilot API in the `X-GitHub-Copilot-Request-TE` response header for the associated model call; `false` when the header was absent or unparseable. #[serde(skip_serializing_if = "Option::is_none")] pub rte: Option, @@ -4130,9 +4191,24 @@ pub struct AssistantUsageData { /// Milliseconds spent on the WebSocket attempt before falling back to HTTP #[serde(skip_serializing_if = "Option::is_none")] pub websocket_fallback_after_ms: Option, + /// WebSocket close code received before the HTTP fallback + #[serde(skip_serializing_if = "Option::is_none")] + pub websocket_fallback_close_code: Option, + /// Normalized cause of the WebSocket failure that triggered the HTTP fallback; absent when no fallback occurred or the cause is unknown + #[serde(skip_serializing_if = "Option::is_none")] + pub websocket_fallback_error_kind: Option, /// Why the call was carried by the HTTP fallback of a WebSocket-capable dispatcher; absent when no fallback occurred #[serde(skip_serializing_if = "Option::is_none")] pub websocket_fallback_reason: Option, + /// Whether a WebSocket reconnect was attempted before falling back to HTTP + #[serde(skip_serializing_if = "Option::is_none")] + pub websocket_fallback_reconnect_attempted: Option, + /// Whether the HTTP fallback started during this model call, rather than being inherited from an earlier call + #[serde(skip_serializing_if = "Option::is_none")] + pub websocket_fallback_started_this_call: Option, + /// HTTP status of the WebSocket upgrade rejection or WebSocket API error that triggered the HTTP fallback + #[serde(skip_serializing_if = "Option::is_none")] + pub websocket_fallback_status_code: Option, } /// Session event "prompt_cache_break". A detected loss of a previously cached prompt prefix @@ -4333,9 +4409,15 @@ pub struct ModelCallFailureData { /// Reasoning effort level used for the failed model call, if applicable #[serde(skip_serializing_if = "Option::is_none")] pub reasoning_effort: Option, - /// Serialized (uncompressed) byte length of the failed request body. A content-free size signal. + /// Serialized (uncompressed) byte length of the failed request body, before any Content-Encoding. A content-free size signal. #[serde(skip_serializing_if = "Option::is_none")] pub request_body_bytes: Option, + /// Content-Encoding applied to the failed request body; identity when uncompressed + #[serde(skip_serializing_if = "Option::is_none")] + pub request_body_encoding: Option, + /// Byte length of the failed request body actually sent on the wire, after any Content-Encoding. Equals requestBodyBytes when the body was not compressed. + #[serde(skip_serializing_if = "Option::is_none")] + pub request_body_wire_bytes: Option, /// Content-free structural summary of the failing request. Contains only counts and shape flags (no prompt content), so it is safe for unrestricted telemetry. Populated only for client-error (4xx) failures. #[serde(skip_serializing_if = "Option::is_none")] pub request_fingerprint: Option, @@ -4359,9 +4441,24 @@ pub struct ModelCallFailureData { /// Milliseconds spent on the WebSocket attempt before falling back to HTTP #[serde(skip_serializing_if = "Option::is_none")] pub websocket_fallback_after_ms: Option, + /// WebSocket close code received before the HTTP fallback + #[serde(skip_serializing_if = "Option::is_none")] + pub websocket_fallback_close_code: Option, + /// Normalized cause of the WebSocket failure that triggered the HTTP fallback; absent when no fallback occurred or the cause is unknown + #[serde(skip_serializing_if = "Option::is_none")] + pub websocket_fallback_error_kind: Option, /// Why the failed call was carried by the HTTP fallback of a WebSocket-capable dispatcher; absent when no fallback occurred #[serde(skip_serializing_if = "Option::is_none")] pub websocket_fallback_reason: Option, + /// Whether a WebSocket reconnect was attempted before falling back to HTTP + #[serde(skip_serializing_if = "Option::is_none")] + pub websocket_fallback_reconnect_attempted: Option, + /// Whether the HTTP fallback started during this model call, rather than being inherited from an earlier call + #[serde(skip_serializing_if = "Option::is_none")] + pub websocket_fallback_started_this_call: Option, + /// HTTP status of the WebSocket upgrade rejection or WebSocket API error that triggered the HTTP fallback + #[serde(skip_serializing_if = "Option::is_none")] + pub websocket_fallback_status_code: Option, } /// Session event "model.call_final_result". Internal telemetry result for one logical model operation after all orchestrator-owned retries settle @@ -6953,6 +7050,12 @@ pub struct PermissionRequestedData { /// Agent mode captured from the owning turn when permission evaluation began. #[serde(skip_serializing_if = "Option::is_none")] pub agent_mode: Option, + /// Delegated agent that owns permission recovery when the owner session multiplexes remote agents. Absent otherwise. + #[serde(skip_serializing_if = "Option::is_none")] + pub owner_agent_id: Option, + /// Execution session that owns permission recovery for this request. Absent on historical events. + #[serde(skip_serializing_if = "Option::is_none")] + pub owner_session_id: Option, /// Permission mode captured when evaluation began. Absent on historical events. #[serde(skip_serializing_if = "Option::is_none")] pub permission_mode: Option, @@ -8002,6 +8105,17 @@ pub struct SessionManagedPluginProgressData { pub plugin_specs: Vec, } +/// Reusable managed permission evaluation context. Treat permissions as runtime-owned policy data and retain it verbatim; it includes source-aware composition metadata. This is a snapshot, not a capability or approval token; only use contexts obtained from trusted policy sources. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ManagedPermissionsContext { + /// Policy could not be determined; evaluation must deny every operation. + pub fail_closed: bool, + /// Runtime-owned composed permissions object. Absent when no permission policy is configured. + #[serde(skip_serializing_if = "Option::is_none")] + pub permissions: Option, +} + /// Session event "session.managed_settings_resolved". Effective enterprise managed settings applied to the session and their contributing channels. Emitted whenever managed policy is applied or reapplied, including session start, resume, and account switch. This ephemeral live snapshot is delivered to subscribers but not persisted to the session event log; initial resolution occurs before session.start. /// ///
@@ -8013,20 +8127,23 @@ pub struct SessionManagedPluginProgressData { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SessionManagedSettingsResolvedData { - /// Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers, and forced on when `failClosed` is true. + /// Whether an explicit enterprise policy restriction disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers; an unresolved policy does not force this on by itself. pub bypass_permissions_disabled: bool, /// Whether a session-local permissions layer injected by the SDK host was present #[serde(skip_serializing_if = "Option::is_none")] pub client_managed: Option, /// Whether an actual device MDM/plist/registry/file managed-settings layer was present pub device_managed: bool, - /// Whether managed policy could not be determined (e.g. a failed server fetch) and the session fell back to the fail-closed restriction. When true, restrictions such as disabling bypass-permissions are enforced even though `settings` may be absent. + /// Whether managed policy could not be determined (e.g. a failed server fetch) and unresolved-policy safeguards remain active. This does not by itself disable bypass-permissions; `bypassPermissionsDisabled` reports only an explicit policy restriction. pub fail_closed: bool, /// The setting keys under enterprise management in the effective managed settings (e.g. `model`, `enabledPlugins`, `permissions`). Empty when no managed settings are in force. pub managed_keys: Vec, /// Whether at least two managed sources supplied permission allowlists, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. #[serde(skip_serializing_if = "Option::is_none")] pub permissions_allow_intersected: Option, + /// Reusable retained managed permission policy for managedSettings.permissions.evaluate, including source composition and session-local injection. Provided by live session snapshots; absent in older events. Refresh the context when managed policy changes. + #[serde(skip_serializing_if = "Option::is_none")] + pub permissions_context: Option, /// Whether the policy-helper managed-settings layer was present. The policy helper is the weakest channel: it fills keys no enterprise source set and can never replace one. #[serde(skip_serializing_if = "Option::is_none")] pub policy_helper_managed: Option, @@ -8887,6 +9004,9 @@ pub enum RemediationAction { /// Permit outbound network access in the sandbox policy. #[serde(rename = "allow_sandbox_outbound")] AllowSandboxOutbound, + /// Discard conversation history back through the blocked request before continuing. + #[serde(rename = "rewind_conversation")] + RewindConversation, /// Unknown variant for forward compatibility. #[default] #[serde(other)] @@ -9452,33 +9572,36 @@ pub enum ProviderQuotaCapacityState { Unknown, } -/// Timing and purpose of a provider quota observation. +/// Identity scope of a monthly usage reading. #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub enum ProviderQuotaObservationKind { - /// An explicit account read; consumption may still be subject to service aggregation delay. - #[serde(rename = "account_snapshot")] - AccountSnapshot, - /// A verdict attached before an inference response. May be cached; never a post-charge balance. - #[serde(rename = "admission_state")] - AdmissionState, +pub enum ProviderMonthlyUsageScope { + /// Consumption of the authenticated user within the parent observation's service and returned cycle. + #[serde(rename = "user")] + User, + /// The service did not establish a recognized usage scope. + #[serde(rename = "unknown")] + UnknownValue, /// Unknown variant for forward compatibility. #[default] #[serde(other)] Unknown, } -/// Interpretation permitted for independently reported quantities. +/// Availability of a service's monthly consumption reading. #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub enum ProviderQuotaQuantityKind { - /// Quantities form an authoritative available-of-entitled budget suitable for percentage presentation. - #[serde(rename = "authoritative_budget")] - AuthoritativeBudget, - /// Raw advisory quantities; do not infer permission, usage percentage, reset cadence, or post-charge balance. - #[serde(rename = "advisory_balance")] - AdvisoryBalance, - /// No quantity measurement, as with admission-only response headers. - #[serde(rename = "none")] - None, +pub enum ProviderMonthlyUsageState { + /// The service reported a usable consumption reading, including an explicit zero. + #[serde(rename = "available")] + Available, + /// The service found no usage policy; no consumption amount is known. + #[serde(rename = "no_policy")] + NoPolicy, + /// The read failed or its data could not be used; consumption is unknown. + #[serde(rename = "unavailable")] + Unavailable, + /// The service reported an unrecognized usage state. + #[serde(rename = "unknown")] + UnknownValue, /// Unknown variant for forward compatibility. #[default] #[serde(other)] @@ -9506,6 +9629,39 @@ pub enum ProviderQuotaUnit { Unknown, } +/// Timing and purpose of a provider quota observation. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ProviderQuotaObservationKind { + /// An explicit account read; consumption may still be subject to service aggregation delay. + #[serde(rename = "account_snapshot")] + AccountSnapshot, + /// A verdict attached before an inference response. May be cached; never a post-charge balance. + #[serde(rename = "admission_state")] + AdmissionState, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// Interpretation permitted for independently reported quantities. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ProviderQuotaQuantityKind { + /// Quantities form an authoritative available-of-entitled budget suitable for percentage presentation. + #[serde(rename = "authoritative_budget")] + AuthoritativeBudget, + /// Raw advisory quantities; do not infer permission, usage percentage, reset cadence, or post-charge balance. + #[serde(rename = "advisory_balance")] + AdvisoryBalance, + /// No quantity measurement, as with admission-only response headers. + #[serde(rename = "none")] + None, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// What initiated a conversation compaction #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum CompactionTrigger { @@ -10108,6 +10264,24 @@ pub enum AssistantUsageApiEndpoint { Unknown, } +/// Content-Encoding applied to the request body sent on the wire +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ModelCallRequestBodyEncoding { + /// The request body was sent uncompressed (including every WebSocket request). + #[serde(rename = "identity")] + Identity, + /// The request body was sent gzip-compressed. + #[serde(rename = "gzip")] + Gzip, + /// The request body was sent zstd-compressed. + #[serde(rename = "zstd")] + Zstd, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Transport used for a successful model call #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum AssistantUsageTransport { @@ -10123,6 +10297,51 @@ pub enum AssistantUsageTransport { Unknown, } +/// Normalized cause of the WebSocket failure that triggered the HTTP fallback +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ModelCallWebSocketFallbackErrorKind { + /// DNS resolution of the WebSocket host failed. + #[serde(rename = "dns")] + Dns, + /// The TLS handshake or a TLS record failed. + #[serde(rename = "tls")] + Tls, + /// A WebSocket connect or read timed out. + #[serde(rename = "timeout")] + Timeout, + /// The peer refused the TCP connection. + #[serde(rename = "connection_refused")] + ConnectionRefused, + /// The connection was reset, aborted, or ended mid-stream without a close. + #[serde(rename = "connection_reset")] + ConnectionReset, + /// The peer closed the WebSocket. + #[serde(rename = "closed_by_peer")] + ClosedByPeer, + /// The runtime closed the WebSocket itself. + #[serde(rename = "closed_locally")] + ClosedLocally, + /// The socket was already closed before it was used. + #[serde(rename = "not_connected")] + NotConnected, + /// The upgrade or request was rejected with an HTTP status. + #[serde(rename = "http_status")] + HttpStatus, + /// A WebSocket protocol violation or an undecodable frame. + #[serde(rename = "protocol")] + Protocol, + /// The WebSocket request could not be built. + #[serde(rename = "configuration")] + Configuration, + /// A failure that maps onto none of the other kinds. + #[serde(rename = "other")] + Other, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Why a WebSocket-capable model call was carried by the HTTP fallback #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum ModelCallWebSocketFallbackReason { diff --git a/rust/src/jsonrpc.rs b/rust/src/jsonrpc.rs index 2f69da5ba9..c602ca9f4d 100644 --- a/rust/src/jsonrpc.rs +++ b/rust/src/jsonrpc.rs @@ -270,13 +270,13 @@ fn repair_lone_surrogates(body: &[u8]) -> Option> { /// /// `frame` is the fully serialized bytes (header + body); the caller pays /// the serde cost synchronously before enqueueing so the actor never sees a -/// `Result` from JSON encoding. `ack` resolves once the bytes have been +/// `Result` from JSON encoding. When present, `ack` resolves once the bytes have been /// fully written and flushed (or the underlying I/O reports an error). If /// the caller drops the `oneshot::Receiver`, the actor still completes the /// frame — caller cancellation cannot desync the wire. struct WriteCommand { frame: Vec, - ack: oneshot::Sender>, + ack: Option>>, } /// Inbound requests that honor `$/cancelRequest`. @@ -412,9 +412,12 @@ impl JsonRpcClient { host_notifications: Option>, ) -> Self { let (write_tx, write_rx) = mpsc::unbounded_channel::(); + let connection_closed = CancellationToken::new(); let writer_span = tracing::error_span!("jsonrpc_write_loop"); - let write_task = tokio::spawn(Self::write_loop(writer, write_rx).instrument(writer_span)); + let write_task = tokio::spawn( + Self::write_loop(writer, write_rx, connection_closed.clone()).instrument(writer_span), + ); let client = Self { request_id: AtomicU64::new(1), @@ -423,7 +426,7 @@ impl JsonRpcClient { notification_tx, request_tx, request_handlers: Arc::new(RwLock::new(HashMap::new())), - connection_closed: CancellationToken::new(), + connection_closed, cancellable_requests: Arc::new(CancellableRequests::default()), read_task: Mutex::new(None), write_task: Mutex::new(Some(write_task)), @@ -518,6 +521,7 @@ impl JsonRpcClient { async fn write_loop( mut writer: impl AsyncWrite + Unpin + Send + 'static, mut rx: mpsc::UnboundedReceiver, + connection_closed: CancellationToken, ) { while let Some(WriteCommand { frame, ack }) = rx.recv().await { let result = async { @@ -526,11 +530,21 @@ impl JsonRpcClient { Ok::<_, std::io::Error>(()) } .await; - // Caller may have dropped the ack receiver (e.g. their // `await` was cancelled); that's fine — we still completed // the write, which was the whole point. - let _ = ack.send(result); + match ack { + Some(ack) => { + let _ = ack.send(result); + } + None => { + if let Err(error) = result { + error!(%error, "router rejection write failed; closing connection"); + connection_closed.cancel(); + break; + } + } + } } } @@ -550,7 +564,12 @@ impl JsonRpcClient { let (connection_closed, cancellable_requests) = connection; loop { - match Self::read_message(&mut reader).await { + let message = tokio::select! { + biased; + _ = connection_closed.cancelled() => break, + message = Self::read_message(&mut reader) => message, + }; + match message { Ok(Some(message)) => match message { JsonRpcMessage::Response(mut response) => { let id = response.id; @@ -904,27 +923,18 @@ impl JsonRpcClient { Self::write_message(&self.write_tx, message).await } + /// Commit a router rejection to the existing writer actor without retaining + /// a delivery waiter. The actor reports write failure and closes the connection. + pub(crate) fn enqueue_response(&self, response: &JsonRpcResponse) -> Result<(), Error> { + Self::enqueue_message(&self.write_tx, response, None) + } + async fn write_message( write_tx: &mpsc::UnboundedSender, message: &T, ) -> Result<(), Error> { - let body = serde_json::to_vec(message)?; - let mut frame = Vec::with_capacity(CONTENT_LENGTH_HEADER.len() + 16 + body.len() + 4); - frame.extend_from_slice(CONTENT_LENGTH_HEADER.as_bytes()); - frame.extend_from_slice(body.len().to_string().as_bytes()); - frame.extend_from_slice(b"\r\n\r\n"); - frame.extend_from_slice(&body); - let (ack_tx, ack_rx) = oneshot::channel(); - write_tx - .send(WriteCommand { frame, ack: ack_tx }) - .map_err(|_| { - Error::from(std::io::Error::new( - std::io::ErrorKind::BrokenPipe, - "writer actor has shut down", - )) - })?; - + Self::enqueue_message(write_tx, message, Some(ack_tx))?; match ack_rx.await { Ok(Ok(())) => Ok(()), Ok(Err(e)) => Err(Error::from(e)), @@ -934,6 +944,28 @@ impl JsonRpcClient { ))), } } + + fn enqueue_message( + write_tx: &mpsc::UnboundedSender, + message: &T, + ack: Option>>, + ) -> Result<(), Error> { + let body = serde_json::to_vec(message)?; + let mut frame = Vec::with_capacity(CONTENT_LENGTH_HEADER.len() + 16 + body.len() + 4); + frame.extend_from_slice(CONTENT_LENGTH_HEADER.as_bytes()); + frame.extend_from_slice(body.len().to_string().as_bytes()); + frame.extend_from_slice(b"\r\n\r\n"); + frame.extend_from_slice(&body); + + write_tx.send(WriteCommand { frame, ack }).map_err(|_| { + Error::from(std::io::Error::new( + std::io::ErrorKind::BrokenPipe, + "writer actor has shut down", + )) + })?; + + Ok(()) + } } impl Drop for JsonRpcClient { diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 93790a94b8..090bbaf85f 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1634,12 +1634,17 @@ impl Client { if !use_logged_in_user { args.push("--no-auto-login".to_string()); } + let integration_id = std::env::var("GITHUB_COPILOT_INTEGRATION_ID") + .ok() + .map(|value| value.trim().to_owned()) + .filter(|value| !value.is_empty()); let explicit_cli = std::env::var_os("COPILOT_CLI_PATH") .map(PathBuf::from) .filter(|path| path.is_file()); let host = crate::ffi::FfiHost::create( &program, explicit_cli.as_deref(), + integration_id.as_deref(), environment, args, )?; diff --git a/rust/src/process_tree.rs b/rust/src/process_tree.rs index ef9d3fc62e..238854bcf9 100644 --- a/rust/src/process_tree.rs +++ b/rust/src/process_tree.rs @@ -56,9 +56,11 @@ mod platform { use std::{io, ptr}; use tokio::process::{Child, Command}; - use windows_sys::Win32::Foundation::{CloseHandle, HANDLE, INVALID_HANDLE_VALUE}; - use windows_sys::Win32::System::Diagnostics::ToolHelp::{ - CreateToolhelp32Snapshot, TH32CS_SNAPTHREAD, THREADENTRY32, Thread32First, Thread32Next, + use windows_sys::Win32::Foundation::{CloseHandle, ERROR_NO_MORE_ITEMS, HANDLE}; + use windows_sys::Win32::System::Diagnostics::ProcessSnapshotting::{ + HPSS, HPSSWALK, PSS_CAPTURE_THREADS, PSS_THREAD_ENTRY, PSS_WALK_THREADS, + PssCaptureSnapshot, PssFreeSnapshot, PssWalkMarkerCreate, PssWalkMarkerFree, + PssWalkSnapshot, }; use windows_sys::Win32::System::JobObjects::{ AssignProcessToJobObject, CreateJobObjectW, JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, @@ -66,9 +68,48 @@ mod platform { SetInformationJobObject, TerminateJobObject, }; use windows_sys::Win32::System::Threading::{ - CREATE_NO_WINDOW, CREATE_SUSPENDED, OpenThread, ResumeThread, THREAD_SUSPEND_RESUME, + CREATE_NO_WINDOW, CREATE_SUSPENDED, GetCurrentProcess, GetProcessIdOfThread, OpenThread, + ResumeThread, THREAD_QUERY_LIMITED_INFORMATION, THREAD_SUSPEND_RESUME, }; + #[cfg(test)] + use super::tests::{ + BEFORE_ASSIGNMENT, BEFORE_RESUME, FailurePoint, MARKERS, RESUME_ELAPSED, SNAPSHOTS, + THREADS_EXAMINED, fail_at, + }; + + struct ThreadSnapshot(HPSS); + + impl Drop for ThreadSnapshot { + fn drop(&mut self) { + // SAFETY: this process uniquely owns the local snapshot descriptor. + let status = unsafe { PssFreeSnapshot(GetCurrentProcess(), self.0) }; + if let Err(error) = check_status(status) { + tracing::warn!(%error, "failed to free CLI thread snapshot"); + } + #[cfg(test)] + if status == 0 { + SNAPSHOTS.set((SNAPSHOTS.get().0, SNAPSHOTS.get().1 + 1)); + } + } + } + + struct WalkMarker(HPSSWALK); + + impl Drop for WalkMarker { + fn drop(&mut self) { + // SAFETY: this value uniquely owns a valid walk marker. + let status = unsafe { PssWalkMarkerFree(self.0) }; + if let Err(error) = check_status(status) { + tracing::warn!(%error, "failed to free CLI thread walk marker"); + } + #[cfg(test)] + if status == 0 { + MARKERS.set((MARKERS.get().0, MARKERS.get().1 + 1)); + } + } + } + struct OwnedHandle(HANDLE); // SAFETY: Win32 handles may be used and closed from any thread. @@ -97,13 +138,19 @@ mod platform { match attach_and_resume(&child) { Ok(tree) => Ok((child, tree)), Err(error) => { - let _ = child.start_kill(); + if let Err(kill_error) = child.start_kill() { + tracing::warn!(%kill_error, "failed to terminate suspended CLI child"); + } Err(error) } } } fn attach_and_resume(child: &Child) -> io::Result { + #[cfg(test)] + if let Some(hook) = BEFORE_ASSIGNMENT.with_borrow_mut(Option::take) { + hook(child)?; + } // SAFETY: null security attributes and name create a private, // non-inheritable Job Object. let raw_job = unsafe { CreateJobObjectW(ptr::null(), ptr::null()) }; @@ -138,50 +185,122 @@ mod platform { return Err(io::Error::last_os_error()); } - resume_initial_thread(child.id().ok_or_else(|| { - io::Error::new(io::ErrorKind::NotFound, "CLI exited before thread resume") - })?)?; + #[cfg(test)] + if let Some(hook) = BEFORE_RESUME.with_borrow_mut(Option::take) { + hook(child, job.0)?; + } + #[cfg(test)] + let resume_start = std::time::Instant::now(); + let result = resume_initial_thread( + process.cast(), + child.id().ok_or_else(|| { + io::Error::new(io::ErrorKind::NotFound, "CLI exited before thread resume") + })?, + ); + #[cfg(test)] + RESUME_ELAPSED.set(resume_start.elapsed()); + result?; Ok(Tree { job }) } - fn resume_initial_thread(pid: u32) -> io::Result<()> { - // SAFETY: the returned snapshot handle is owned and closed below. - let snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0) }; - if snapshot == INVALID_HANDLE_VALUE { - return Err(io::Error::last_os_error()); + pub(super) fn check_status(status: u32) -> io::Result<()> { + if status == 0 { + Ok(()) + } else { + Err(io::Error::from_raw_os_error(status as i32)) } - let snapshot = OwnedHandle(snapshot); - let mut entry = THREADENTRY32 { - dwSize: size_of::() as u32, - ..Default::default() - }; - - // SAFETY: `entry` has the documented size and remains live throughout - // enumeration. - let mut found = unsafe { Thread32First(snapshot.0, &mut entry) } != 0; - while found { - if entry.th32OwnerProcessID == pid { - // SAFETY: the thread id came from the live system snapshot. - let raw_thread = - unsafe { OpenThread(THREAD_SUSPEND_RESUME, 0, entry.th32ThreadID) }; + } + + pub(super) fn resume_initial_thread(process: HANDLE, pid: u32) -> io::Result<()> { + #[cfg(test)] + THREADS_EXAMINED.set(0); + #[cfg(test)] + fail_at(FailurePoint::Capture)?; + let mut snapshot = ptr::null_mut(); + // SAFETY: the suspended child owns the live process handle throughout + // capture and resume. Only its thread information is captured. + check_status(unsafe { + PssCaptureSnapshot(process, PSS_CAPTURE_THREADS, 0, &mut snapshot) + })?; + let snapshot = ThreadSnapshot(snapshot); + #[cfg(test)] + SNAPSHOTS.set((SNAPSHOTS.get().0 + 1, SNAPSHOTS.get().1)); + + #[cfg(test)] + fail_at(FailurePoint::Marker)?; + let mut marker = ptr::null_mut(); + // SAFETY: Windows initializes the marker with its default allocator. + check_status(unsafe { PssWalkMarkerCreate(ptr::null(), &mut marker) })?; + let marker = WalkMarker(marker); + #[cfg(test)] + MARKERS.set((MARKERS.get().0 + 1, MARKERS.get().1)); + + loop { + #[cfg(test)] + fail_at(FailurePoint::Walk)?; + let mut entry = PSS_THREAD_ENTRY::default(); + // SAFETY: the aligned entry has the layout and size required by + // PSS_WALK_THREADS; both snapshot and marker remain owned. + let status = unsafe { + PssWalkSnapshot( + snapshot.0, + PSS_WALK_THREADS, + marker.0, + ptr::from_mut(&mut entry).cast(), + size_of::() as u32, + ) + }; + if status == ERROR_NO_MORE_ITEMS { + return Err(io::Error::new( + io::ErrorKind::NotFound, + "CLI initial thread was not found in its process snapshot", + )); + } + check_status(status)?; + #[cfg(test)] + THREADS_EXAMINED.set(THREADS_EXAMINED.get() + 1); + if entry.ProcessId == pid { + #[cfg(test)] + fail_at(FailurePoint::OpenThread)?; + // SAFETY: the thread id came from the child's process snapshot. + let raw_thread = unsafe { + OpenThread( + THREAD_SUSPEND_RESUME | THREAD_QUERY_LIMITED_INFORMATION, + 0, + entry.ThreadId, + ) + }; if raw_thread.is_null() { return Err(io::Error::last_os_error()); } let thread = OwnedHandle(raw_thread); - // SAFETY: this is the root's suspended initial thread. - if unsafe { ResumeThread(thread.0) } == u32::MAX { - return Err(io::Error::last_os_error()); - } - return Ok(()); + return resume_owned_thread(thread.0, pid); } - // SAFETY: same valid snapshot and initialized entry as above. - found = unsafe { Thread32Next(snapshot.0, &mut entry) } != 0; } + } - Err(io::Error::new( - io::ErrorKind::NotFound, - "CLI initial thread was not found", - )) + pub(super) fn resume_owned_thread(thread: HANDLE, pid: u32) -> io::Result<()> { + #[cfg(test)] + fail_at(FailurePoint::QueryThread)?; + // SAFETY: the caller owns this thread handle with limited-query rights. + let owner = unsafe { GetProcessIdOfThread(thread) }; + if owner == 0 { + return Err(io::Error::last_os_error()); + } + if owner != pid { + return Err(io::Error::new( + io::ErrorKind::PermissionDenied, + "CLI initial thread no longer belongs to the spawned process", + )); + } + #[cfg(test)] + fail_at(FailurePoint::Resume)?; + // SAFETY: the open handle's ownership was verified, and the root's + // initial thread remains suspended until Job assignment completes. + if unsafe { ResumeThread(thread) } == u32::MAX { + return Err(io::Error::last_os_error()); + } + Ok(()) } impl Tree { @@ -195,3 +314,6 @@ mod platform { } } } + +#[cfg(all(test, windows, feature = "runtime"))] +mod tests; diff --git a/rust/src/process_tree/tests.rs b/rust/src/process_tree/tests.rs new file mode 100644 index 0000000000..5106081dac --- /dev/null +++ b/rust/src/process_tree/tests.rs @@ -0,0 +1,318 @@ +#![cfg(test)] + +use std::os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle}; +use std::os::windows::process::CommandExt; +use std::path::{Path, PathBuf}; +use std::sync::Arc; +use std::time::{Duration, Instant}; +use std::{io, ptr}; + +use tokio::process::{Child, Command}; +use windows_sys::Win32::Foundation::{ + DUPLICATE_SAME_ACCESS, DuplicateHandle, ERROR_ACCESS_DENIED, HANDLE, +}; +use windows_sys::Win32::System::JobObjects::IsProcessInJob; +use windows_sys::Win32::System::Threading::{ + GetCurrentProcess, GetCurrentProcessId, GetCurrentThreadId, OpenThread, + THREAD_QUERY_LIMITED_INFORMATION, THREAD_SUSPEND_RESUME, WaitForSingleObject, +}; + +use super::platform::{check_status, resume_initial_thread, resume_owned_thread}; + +#[derive(Clone, Copy, PartialEq)] +pub(super) enum FailurePoint { + Capture, + Marker, + Walk, + OpenThread, + QueryThread, + Resume, +} + +type BeforeAssignment = Box io::Result<()>>; +type BeforeResume = Box io::Result<()>>; + +thread_local! { + pub(super) static THREADS_EXAMINED: std::cell::Cell = const { std::cell::Cell::new(0) }; + pub(super) static RESUME_ELAPSED: std::cell::Cell = + const { std::cell::Cell::new(Duration::ZERO) }; + pub(super) static BEFORE_ASSIGNMENT: std::cell::RefCell> = + const { std::cell::RefCell::new(None) }; + pub(super) static BEFORE_RESUME: std::cell::RefCell> = + const { std::cell::RefCell::new(None) }; + static FAILURE: std::cell::Cell> = + const { std::cell::Cell::new(None) }; + pub(super) static SNAPSHOTS: std::cell::Cell<(u32, u32)> = + const { std::cell::Cell::new((0, 0)) }; + pub(super) static MARKERS: std::cell::Cell<(u32, u32)> = + const { std::cell::Cell::new((0, 0)) }; +} + +pub(super) fn fail_at(point: FailurePoint) -> io::Result<()> { + if FAILURE.get() == Some(point) { + Err(io::Error::from_raw_os_error(ERROR_ACCESS_DENIED as i32)) + } else { + Ok(()) + } +} + +struct ResetHooks; + +impl Drop for ResetHooks { + fn drop(&mut self) { + FAILURE.set(None); + BEFORE_ASSIGNMENT.with_borrow_mut(|hook| *hook = None); + BEFORE_RESUME.with_borrow_mut(|hook| *hook = None); + } +} + +fn command() -> Command { + let mut command = Command::new( + PathBuf::from(std::env::var_os("SystemRoot").expect("SystemRoot")) + .join("System32") + .join("cmd.exe"), + ); + command.args(["/d", "/c"]).kill_on_drop(true); + command +} + +fn marker_command(marker: &Path) -> Command { + let mut command = command(); + command + .as_std_mut() + .raw_arg(format!("echo ready>\"{}\"", marker.display())); + command +} + +fn retain_process(child: &Child) -> OwnedHandle { + let mut handle = ptr::null_mut(); + // SAFETY: the child owns its live process handle; the duplicate is independently owned. + let result = unsafe { + DuplicateHandle( + GetCurrentProcess(), + child.raw_handle().expect("live child").cast(), + GetCurrentProcess(), + &mut handle, + 0, + 0, + DUPLICATE_SAME_ACCESS, + ) + }; + assert_ne!(result, 0, "{}", io::Error::last_os_error()); + // SAFETY: DuplicateHandle returned a new uniquely owned process handle. + unsafe { OwnedHandle::from_raw_handle(handle) } +} + +fn assert_exited(handle: &OwnedHandle) { + // SAFETY: the independently owned process handle remains valid. + assert_eq!( + unsafe { WaitForSingleObject(handle.as_raw_handle().cast(), 5000) }, + 0, + "owned suspended root must terminate" + ); +} + +#[tokio::test] +async fn initial_thread_lookup_is_process_scoped() { + let mut command = command(); + command.args(["exit", "/b", "0"]); + let start = Instant::now(); + let (mut child, tree) = super::spawn(&mut command).expect("spawn owned suspended child"); + let spawn_elapsed = start.elapsed(); + let status = tokio::time::timeout(Duration::from_secs(5), child.wait()) + .await + .expect("initial thread must execute") + .expect("reap child"); + drop(tree); + assert!(status.success()); + eprintln!( + "owned lookup: spawn_ms={:.3} resume_ms={:.3} entries={}", + spawn_elapsed.as_secs_f64() * 1000.0, + RESUME_ELAPSED.get().as_secs_f64() * 1000.0, + THREADS_EXAMINED.get() + ); + assert_eq!( + THREADS_EXAMINED.get(), + 1, + "lookup must not walk unrelated machine-wide threads" + ); +} + +#[tokio::test] +async fn initial_thread_executes_only_after_private_job_assignment() { + let _reset = ResetHooks; + let dir = tempfile::tempdir().expect("fixture directory"); + let marker = dir.path().join("executed.txt"); + BEFORE_ASSIGNMENT.with_borrow_mut(|hook| { + let marker = marker.clone(); + *hook = Some(Box::new(move |_| { + assert!( + !marker.exists(), + "suspended root cannot execute before assignment" + ); + Ok(()) + })); + }); + BEFORE_RESUME.with_borrow_mut(|hook| { + let marker = marker.clone(); + *hook = Some(Box::new(move |child, job| { + assert!( + !marker.exists(), + "suspended initial thread must not have executed" + ); + let mut assigned = 0; + // SAFETY: both handles remain owned throughout the hook. + assert_ne!( + unsafe { + IsProcessInJob( + child.raw_handle().expect("live child").cast(), + job, + &mut assigned, + ) + }, + 0, + "{}", + io::Error::last_os_error() + ); + assert_ne!( + assigned, 0, + "root must belong to its private Job before resume" + ); + Ok(()) + })); + }); + let (mut child, tree) = super::spawn(&mut marker_command(&marker)).expect("owned spawn"); + let status = tokio::time::timeout(Duration::from_secs(5), child.wait()) + .await + .expect("fixture must execute its initial thread") + .expect("reap fixture"); + assert!(status.success()); + assert_eq!( + std::fs::read_to_string(marker) + .expect("executed marker") + .trim(), + "ready" + ); + drop(tree); +} + +#[tokio::test] +async fn lookup_failures_reap_suspended_root_and_release_pss_resources() { + let _reset = ResetHooks; + for point in [ + FailurePoint::Capture, + FailurePoint::Marker, + FailurePoint::Walk, + FailurePoint::OpenThread, + FailurePoint::QueryThread, + FailurePoint::Resume, + ] { + let dir = tempfile::tempdir().expect("fixture directory"); + let marker = dir.path().join("must-not-execute.txt"); + let retained = Arc::new(parking_lot::Mutex::new(None)); + BEFORE_ASSIGNMENT.with_borrow_mut(|hook| { + let retained = retained.clone(); + *hook = Some(Box::new(move |child| { + *retained.lock() = Some(retain_process(child)); + Ok(()) + })); + }); + SNAPSHOTS.set((0, 0)); + MARKERS.set((0, 0)); + FAILURE.set(Some(point)); + let result = super::spawn(&mut marker_command(&marker)); + let error = result.err().expect("injected launch failure"); + assert_eq!(error.raw_os_error(), Some(ERROR_ACCESS_DENIED as i32)); + assert_exited(retained.lock().as_ref().expect("retained suspended root")); + assert!( + !marker.exists(), + "failed lookup must never execute the root" + ); + let snapshots = u32::from(point != FailurePoint::Capture); + let markers = u32::from(point != FailurePoint::Capture && point != FailurePoint::Marker); + assert_eq!(SNAPSHOTS.get(), (snapshots, snapshots)); + assert_eq!(MARKERS.get(), (markers, markers)); + } +} + +#[tokio::test] +async fn pre_assignment_failure_terminates_suspended_root() { + let _reset = ResetHooks; + let dir = tempfile::tempdir().expect("fixture directory"); + let marker = dir.path().join("must-not-execute.txt"); + let retained = Arc::new(parking_lot::Mutex::new(None)); + BEFORE_ASSIGNMENT.with_borrow_mut(|hook| { + let retained = retained.clone(); + *hook = Some(Box::new(move |child| { + *retained.lock() = Some(retain_process(child)); + Err(io::Error::from_raw_os_error(ERROR_ACCESS_DENIED as i32)) + })); + }); + let error = super::spawn(&mut marker_command(&marker)) + .err() + .expect("assignment failure"); + assert_eq!(error.raw_os_error(), Some(ERROR_ACCESS_DENIED as i32)); + assert_exited(retained.lock().as_ref().expect("retained root")); + assert!(!marker.exists()); +} + +#[test] +fn pss_status_uses_returned_win32_error() { + assert!(check_status(0).is_ok()); + assert_eq!( + check_status(ERROR_ACCESS_DENIED) + .expect_err("direct status error") + .raw_os_error(), + Some(ERROR_ACCESS_DENIED as i32) + ); +} + +#[test] +fn invalid_process_capture_returns_error_without_resources() { + let _reset = ResetHooks; + SNAPSHOTS.set((0, 0)); + MARKERS.set((0, 0)); + assert!(resume_initial_thread(ptr::null_mut(), 1).is_err()); + assert_eq!(SNAPSHOTS.get(), (0, 0)); + assert_eq!(MARKERS.get(), (0, 0)); +} + +#[test] +fn foreign_snapshot_entries_cannot_be_resumed() { + let _reset = ResetHooks; + SNAPSHOTS.set((0, 0)); + MARKERS.set((0, 0)); + // SAFETY: the pseudo handle references the current live process. + let error = resume_initial_thread(unsafe { GetCurrentProcess() }, u32::MAX) + .expect_err("no matching owned thread"); + assert_eq!(error.kind(), io::ErrorKind::NotFound); + assert_eq!(SNAPSHOTS.get(), (1, 1)); + assert_eq!(MARKERS.get(), (1, 1)); +} + +#[test] +fn opened_foreign_thread_is_rejected_before_resume() { + let _reset = ResetHooks; + // SAFETY: the thread ID identifies this live test thread. + let handle = unsafe { + OpenThread( + THREAD_QUERY_LIMITED_INFORMATION | THREAD_SUSPEND_RESUME, + 0, + GetCurrentThreadId(), + ) + }; + assert!(!handle.is_null(), "{}", io::Error::last_os_error()); + // SAFETY: OpenThread returned a new uniquely owned handle. + let thread = unsafe { OwnedHandle::from_raw_handle(handle) }; + let error = resume_owned_thread(thread.as_raw_handle().cast(), unsafe { + GetCurrentProcessId().wrapping_add(1) + }) + .expect_err("opened handle belongs to a different process"); + assert_eq!(error.kind(), io::ErrorKind::PermissionDenied); +} + +#[test] +fn invalid_thread_ownership_query_returns_error() { + let _reset = ResetHooks; + assert!(resume_owned_thread(ptr::null_mut(), 1).is_err()); +} diff --git a/rust/src/router.rs b/rust/src/router.rs index 9b0d68464b..dff61da6b1 100644 --- a/rust/src/router.rs +++ b/rust/src/router.rs @@ -302,8 +302,17 @@ impl SessionRouter { // Request routing task (if request_rx is available) if let Some(mut rx) = client.request_rx.lock().take() { let sessions = self.sessions.clone(); + let connection_closed = client.rpc.connection_closed_token(); tokio::spawn(async move { - while let Some(request) = rx.recv().await { + loop { + let request = tokio::select! { + biased; + _ = connection_closed.cancelled() => break, + request = rx.recv() => { + let Some(request) = request else { break }; + request + } + }; if request.method == crate::installation_confirmation::CONFIRM_METHOD { installation_confirmation.dispatch(request); continue; @@ -341,13 +350,14 @@ impl SessionRouter { .map(str::to_owned); let Some(session_id) = session_id else { warn!(method = %request.method, "request missing sessionId"); - send_error( + if !send_error( &weak_client, request.id, error_codes::INVALID_PARAMS, "missing required field: sessionId".to_string(), - ) - .await; + ) { + break; + } continue; }; @@ -368,43 +378,48 @@ impl SessionRouter { method = %request.method, "request for unregistered session" ); - send_error( + if !send_error( &weak_client, request.id, error_codes::INTERNAL_ERROR, format!("Session not found: {session_id}"), - ) - .await; + ) { + break; + } } } + sessions.lock().clear(); }); } } } -async fn send_error( +fn send_error( client: &Weak, request_id: u64, code: i32, message: String, -) { +) -> bool { let Some(inner) = client.upgrade() else { - return; + return false; }; // Retire any cancellation registered for a request that never reaches a handler. drop(inner.rpc.cancellable_requests.claim(request_id)); - let _ = crate::Client::from_inner(inner) - .send_response(&JsonRpcResponse { - jsonrpc: "2.0".to_string(), - id: request_id, - result: None, - error: Some(JsonRpcError { - code, - message, - data: None, - }), - }) - .await; + if let Err(error) = inner.rpc.enqueue_response(&JsonRpcResponse { + jsonrpc: "2.0".to_string(), + id: request_id, + result: None, + error: Some(JsonRpcError { + code, + message, + data: None, + }), + }) { + warn!(%error, "failed to enqueue router rejection; closing connection"); + crate::Client::from_inner(inner).force_stop(); + return false; + } + true } #[cfg(test)] diff --git a/rust/src/router/tests.rs b/rust/src/router/tests.rs index f63ca005f7..997829a50a 100644 --- a/rust/src/router/tests.rs +++ b/rust/src/router/tests.rs @@ -4,8 +4,501 @@ #![cfg(test)] +use std::pin::Pin; +use std::sync::atomic::AtomicBool; +use std::task::{Context, Poll}; +use std::time::Duration; + +use futures_util::task::AtomicWaker; +use serde_json::json; +use tokio::io::{ + AsyncBufReadExt, AsyncReadExt, AsyncWrite, AsyncWriteExt, BufReader, DuplexStream, +}; +use tokio::time::timeout; +use tokio_util::sync::CancellationToken; + use super::*; +const DEADLINE: Duration = Duration::from_secs(5); + +#[derive(Clone, Copy, Debug)] +enum BlockAt { + Write, + Flush, +} + +#[derive(Default)] +struct WriteGate { + released: AtomicBool, + waker: AtomicWaker, + entered: CancellationToken, + dropped: CancellationToken, +} + +impl WriteGate { + fn poll(&self, cx: &mut Context<'_>) -> Poll<()> { + self.waker.register(cx.waker()); + if self.released.load(Ordering::Acquire) { + Poll::Ready(()) + } else { + self.entered.cancel(); + Poll::Pending + } + } + + fn release(&self) { + self.released.store(true, Ordering::Release); + self.waker.wake(); + } +} + +struct GatedWriter { + writer: DuplexStream, + gate: Arc, + block_at: BlockAt, +} + +impl AsyncWrite for GatedWriter { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + bytes: &[u8], + ) -> Poll> { + if matches!(self.block_at, BlockAt::Write) && self.gate.poll(cx).is_pending() { + return Poll::Pending; + } + Pin::new(&mut self.writer).poll_write(cx, bytes) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if matches!(self.block_at, BlockAt::Flush) && self.gate.poll(cx).is_pending() { + return Poll::Pending; + } + Pin::new(&mut self.writer).poll_flush(cx) + } + + fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.writer).poll_shutdown(cx) + } +} + +impl Drop for GatedWriter { + fn drop(&mut self) { + self.gate.dropped.cancel(); + } +} + +#[derive(Clone, Copy, Debug)] +enum Unroutable { + MissingSessionId, + UnknownSession, + ClosedReceiver, +} + +struct Peer { + client: crate::Client, + input: DuplexStream, + output: BufReader, + gate: Arc, +} + +impl Peer { + fn new(block_at: BlockAt) -> Self { + let (reader, input) = tokio::io::duplex(4096); + let (writer, output) = tokio::io::duplex(4096); + let gate = Arc::new(WriteGate::default()); + let client = crate::Client::from_streams( + reader, + GatedWriter { + writer, + gate: gate.clone(), + block_at, + }, + std::path::PathBuf::from("."), + ) + .unwrap(); + Self { + client, + input, + output: BufReader::new(output), + gate, + } + } + + async fn send(&mut self, message: &impl serde::Serialize) { + let body = serde_json::to_vec(message).unwrap(); + timeout(DEADLINE, async { + self.input + .write_all(format!("Content-Length: {}\r\n\r\n", body.len()).as_bytes()) + .await + .unwrap(); + self.input.write_all(&body).await.unwrap(); + }) + .await + .expect("peer could not inject a complete request"); + } + + async fn reject(&mut self, id: u64, kind: Unroutable) { + let params = match kind { + Unroutable::MissingSessionId => json!({}), + Unroutable::UnknownSession => json!({"sessionId": "gone"}), + Unroutable::ClosedReceiver => { + drop(self.client.register_session(&SessionId::new("closed"))); + json!({"sessionId": "closed"}) + } + }; + self.send(&JsonRpcRequest::new(id, "skillProvider.list", Some(params))) + .await; + } + + async fn read(&mut self) -> JsonRpcResponse { + timeout(DEADLINE, async { + let mut header = String::new(); + self.output.read_line(&mut header).await.unwrap(); + let length = header + .strip_prefix("Content-Length: ") + .expect("missing response header") + .trim() + .parse::() + .unwrap(); + header.clear(); + self.output.read_line(&mut header).await.unwrap(); + assert_eq!(header, "\r\n"); + let mut body = vec![0; length]; + self.output.read_exact(&mut body).await.unwrap(); + serde_json::from_slice(&body).unwrap() + }) + .await + .expect("error frame was not delivered") + } + + async fn stop(&self) { + self.client.force_stop(); + timeout(DEADLINE, self.gate.dropped.cancelled()) + .await + .expect("writer did not exit"); + } +} + +#[track_caller] +fn assert_error(response: JsonRpcResponse, id: u64, kind: Unroutable) { + assert_eq!(response.id, id); + assert!(response.result.is_none()); + let error = response.error.expect("unroutable request received success"); + let (code, message) = match kind { + Unroutable::MissingSessionId => ( + error_codes::INVALID_PARAMS, + "missing required field: sessionId", + ), + Unroutable::UnknownSession => (error_codes::INTERNAL_ERROR, "Session not found: gone"), + Unroutable::ClosedReceiver => (error_codes::INTERNAL_ERROR, "Session not found: closed"), + }; + assert_eq!(error.code, code); + assert_eq!(error.message, message); +} + +#[tokio::test] +async fn stalled_rejection_preserves_live_routing_and_eventual_error_delivery() { + for block_at in [BlockAt::Write, BlockAt::Flush] { + for kind in [ + Unroutable::MissingSessionId, + Unroutable::UnknownSession, + Unroutable::ClosedReceiver, + ] { + let mut peer = Peer::new(block_at); + let mut live = peer + .client + .register_session(&SessionId::new("live")) + .channels + .requests; + peer.reject(101, kind).await; + timeout(DEADLINE, peer.gate.entered.cancelled()) + .await + .unwrap(); + peer.send(&crate::JsonRpcNotification { + jsonrpc: "2.0".into(), + method: "$/cancelRequest".into(), + params: Some(json!({"id": 101})), + }) + .await; + peer.send(&JsonRpcRequest::new( + 102, + "skillProvider.list", + Some(json!({"sessionId": "live"})), + )) + .await; + let routed = timeout(DEADLINE, live.recv()) + .await + .unwrap_or_else(|_| panic!("live callback stalled behind {kind:?} at {block_at:?}")) + .expect("live channel closed"); + assert_eq!(routed.id, 102); + assert!( + peer.client + .inner + .rpc + .cancellable_requests + .claim(101) + .is_none() + ); + assert!( + !peer + .client + .inner + .rpc + .connection_closed_token() + .is_cancelled() + ); + peer.gate.release(); + assert_error(peer.read().await, 101, kind); + peer.stop().await; + } + } +} + +#[tokio::test] +async fn delayed_writer_preserves_a_large_healthy_burst_without_disconnect() { + const BURST: u64 = 512; + for block_at in [BlockAt::Write, BlockAt::Flush] { + let mut peer = Peer::new(block_at); + let mut live = peer + .client + .register_session(&SessionId::new("live")) + .channels + .requests; + peer.reject(100, Unroutable::UnknownSession).await; + timeout(DEADLINE, peer.gate.entered.cancelled()) + .await + .unwrap(); + for id in 101..100 + BURST { + peer.reject(id, Unroutable::UnknownSession).await; + } + peer.send(&JsonRpcRequest::new( + 100 + BURST, + "skillProvider.list", + Some(json!({"sessionId": "live"})), + )) + .await; + assert_eq!( + timeout(DEADLINE, live.recv()).await.unwrap().unwrap().id, + 100 + BURST + ); + assert!( + !peer + .client + .inner + .rpc + .connection_closed_token() + .is_cancelled() + ); + for id in 100..100 + BURST { + assert!( + peer.client + .inner + .rpc + .cancellable_requests + .claim(id) + .is_none() + ); + } + peer.gate.release(); + for id in 100..100 + BURST { + assert_error(peer.read().await, id, Unroutable::UnknownSession); + } + peer.stop().await; + } +} + +#[tokio::test] +async fn pending_rejection_does_not_retain_the_last_client() { + for block_at in [BlockAt::Write, BlockAt::Flush] { + let mut peer = Peer::new(block_at); + let mut live = peer + .client + .register_session(&SessionId::new("live")) + .channels + .requests; + peer.reject(101, Unroutable::UnknownSession).await; + timeout(DEADLINE, peer.gate.entered.cancelled()) + .await + .unwrap(); + let weak = Arc::downgrade(&peer.client.inner); + let gate = peer.gate.clone(); + drop(peer); + assert!( + weak.upgrade().is_none(), + "error delivery retained the client" + ); + timeout(DEADLINE, gate.dropped.cancelled()).await.unwrap(); + assert!(timeout(DEADLINE, live.recv()).await.unwrap().is_none()); + } +} + +#[tokio::test] +async fn force_stop_releases_pending_rejections() { + for kind in [ + Unroutable::MissingSessionId, + Unroutable::UnknownSession, + Unroutable::ClosedReceiver, + ] { + let mut peer = Peer::new(BlockAt::Write); + let mut live = peer + .client + .register_session(&SessionId::new("live")) + .channels + .requests; + peer.reject(101, kind).await; + timeout(DEADLINE, peer.gate.entered.cancelled()) + .await + .unwrap(); + peer.stop().await; + assert!(timeout(DEADLINE, live.recv()).await.unwrap().is_none()); + assert!( + peer.client + .inner + .rpc + .cancellable_requests + .claim(101) + .is_none() + ); + } +} + +#[tokio::test] +async fn client_stop_does_not_await_router_rejection_delivery() { + for block_at in [BlockAt::Write, BlockAt::Flush] { + let mut peer = Peer::new(block_at); + peer.client.inner.router.ensure_started(&peer.client.inner); + peer.reject(101, Unroutable::UnknownSession).await; + timeout(DEADLINE, peer.gate.entered.cancelled()) + .await + .unwrap(); + timeout(DEADLINE, peer.client.stop()) + .await + .expect("stop awaited rejection output") + .unwrap(); + timeout(DEADLINE, peer.gate.dropped.cancelled()) + .await + .unwrap(); + } +} + +#[tokio::test] +async fn late_rejection_delivery_does_not_retire_a_reused_request_id() { + let mut peer = Peer::new(BlockAt::Write); + let mut live = peer + .client + .register_session(&SessionId::new("live")) + .channels + .requests; + peer.reject(101, Unroutable::UnknownSession).await; + timeout(DEADLINE, peer.gate.entered.cancelled()) + .await + .unwrap(); + peer.send(&JsonRpcRequest::new( + 101, + "skillProvider.list", + Some(json!({"sessionId": "live"})), + )) + .await; + assert_eq!( + timeout(DEADLINE, live.recv()).await.unwrap().unwrap().id, + 101 + ); + let pending = peer + .client + .inner + .rpc + .cancellable_requests + .claim(101) + .expect("new request lost cancellation"); + peer.gate.release(); + assert_error(peer.read().await, 101, Unroutable::UnknownSession); + peer.send(&crate::JsonRpcNotification { + jsonrpc: "2.0".into(), + method: "$/cancelRequest".into(), + params: Some(json!({"id": 101})), + }) + .await; + timeout(DEADLINE, pending.cancellation().cancelled()) + .await + .expect("old completion retired new cancellation"); + peer.stop().await; +} + +#[tokio::test] +async fn failed_router_rejection_write_closes_connection_and_wakes_pending_work() { + let mut peer = Peer::new(BlockAt::Write); + let mut live = peer + .client + .register_session(&SessionId::new("live")) + .channels + .requests; + peer.reject(101, Unroutable::UnknownSession).await; + timeout(DEADLINE, peer.gate.entered.cancelled()) + .await + .unwrap(); + let pending = peer + .client + .inner + .rpc + .send_request_with_inline_callback("ping", None, None); + tokio::pin!(pending); + assert!(futures_util::poll!(&mut pending).is_pending()); + let Peer { + client, + input: _input, + output, + gate, + } = peer; + drop(output); + gate.release(); + timeout( + DEADLINE, + client.inner.rpc.connection_closed_token().cancelled(), + ) + .await + .expect("write failure left connection open"); + let error = timeout(DEADLINE, pending) + .await + .expect("pending request did not wake") + .expect_err("request succeeded on broken output"); + assert_eq!(error.kind(), &crate::ErrorKind::Io); + assert!(timeout(DEADLINE, live.recv()).await.unwrap().is_none()); + timeout(DEADLINE, gate.dropped.cancelled()).await.unwrap(); + client.force_stop(); +} + +#[tokio::test] +async fn peer_eof_retires_routing_with_a_committed_rejection_pending() { + let mut peer = Peer::new(BlockAt::Write); + let mut live = peer + .client + .register_session(&SessionId::new("live")) + .channels + .requests; + peer.reject(101, Unroutable::UnknownSession).await; + timeout(DEADLINE, peer.gate.entered.cancelled()) + .await + .unwrap(); + peer.input.shutdown().await.unwrap(); + timeout( + DEADLINE, + peer.client.inner.rpc.connection_closed_token().cancelled(), + ) + .await + .unwrap(); + assert!(timeout(DEADLINE, live.recv()).await.unwrap().is_none()); + assert!( + peer.client + .inner + .rpc + .cancellable_requests + .claim(101) + .is_none() + ); + peer.stop().await; +} + fn session_id() -> SessionId { SessionId::new("router-ownership") } diff --git a/rust/src/tests.rs b/rust/src/tests.rs index 8656d6f4d3..bd4d519b87 100644 --- a/rust/src/tests.rs +++ b/rust/src/tests.rs @@ -6,6 +6,61 @@ use super::*; +#[tokio::test] +async fn settings_apply_top_key_preserves_value_presence_on_wire() { + use crate::generated::api_types::UserSettingsApplyTopKeyRequest; + + for (name, value, delete) in [ + ("omitted", None, Some(false)), + ("explicit null", Some(serde_json::Value::Null), None), + ("value", Some(serde_json::json!("chosen")), None), + ("delete", None, Some(true)), + ] { + let temp = tempfile::tempdir().unwrap(); + let (client_stream, server_stream) = tokio::io::duplex(8192); + let (client_read, client_write) = tokio::io::split(client_stream); + let (server_read, server_write) = tokio::io::split(server_stream); + let client = + Client::from_streams(client_read, client_write, temp.path().to_owned()).unwrap(); + let (notifications, _) = tokio::sync::broadcast::channel(1); + let (requests, _) = tokio::sync::mpsc::unbounded_channel(); + let server = + jsonrpc::JsonRpcClient::new(server_write, server_read, notifications, requests); + let (captured, mut received) = tokio::sync::mpsc::unbounded_channel(); + server + .register_request_handler( + "user.settings.applyTopKey", + Arc::new(move |params| { + captured.send(params).unwrap(); + Box::pin(async { Ok(serde_json::json!({ "status": "ok" })) }) + }), + ) + .unwrap(); + + let result = tokio::time::timeout( + Duration::from_secs(5), + client + .rpc() + .user() + .settings() + .apply_top_key(UserSettingsApplyTopKeyRequest { + canonical_path: vec!["model".to_owned()], + top_key: "model".to_owned(), + value: value.clone(), + delete, + complete_staff_update_channel_migration: None, + }), + ) + .await; + client.force_stop(); + server.force_close(); + + result.expect(name).expect(name); + let params = received.try_recv().expect(name); + assert_eq!(params.get("value"), value.as_ref(), "{name}: {params}"); + } +} + #[test] fn is_transport_failure_matches_request_cancelled() { let err = Error::from(ErrorKind::Protocol(ProtocolErrorKind::RequestCancelled)); diff --git a/rust/src/types.rs b/rust/src/types.rs index ba9c8a7451..8f027e56dd 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -667,6 +667,9 @@ impl Serialize for CommandDefinition { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct CustomAgentConfig { + /// Declarative hook action objects keyed by event name, active only for this agent. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub hooks: Option>>>, /// Unique name of the custom agent. pub name: String, /// Display name for UI purposes. @@ -704,6 +707,12 @@ pub struct CustomAgentConfig { } impl CustomAgentConfig { + /// Set declarative hook action objects scoped to this agent. + pub fn with_hooks(mut self, hooks: IndexMap>>) -> Self { + self.hooks = Some(hooks); + self + } + /// Construct a custom agent configuration with the required `name` /// and `prompt` fields populated. /// @@ -838,6 +847,35 @@ impl LargeToolOutputConfig { } } +/// Opt in to image generation through an authorized Copilot image model. +/// +/// **Experimental.** This API may change or be removed in a future release. +/// +/// Omission disables on create/cold resume and preserves resident resume state. +/// Not persisted: re-supply after runtime restart. Policy, permissions, offline +/// mode, and tool filters still apply. BYOK image generation is not supported. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct ImageGenerationConfig { + /// True opts in; false disables image generation. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub enabled: Option, +} + +impl ImageGenerationConfig { + /// Construct an unset image generation configuration. + pub fn new() -> Self { + Self::default() + } + + /// Enable or disable image generation. + pub fn with_enabled(mut self, enabled: bool) -> Self { + self.enabled = Some(enabled); + self + } +} + /// Overrides the runtime's built-in tool-search behavior. /// /// Tool search defers tools to keep the model's active tool set small. @@ -2024,6 +2062,13 @@ pub enum AskUserVariant { pub struct SessionConfig { /// Custom session ID. When unset, the CLI generates one. pub session_id: Option, + /// Name the session at creation, as `session.name.set` would, and validated + /// the same way: non-empty, no leading or trailing whitespace, at most 100 + /// UTF-16 units, no control characters, and no double quotes. A name also + /// makes the session persist from the start, so it can be listed and resumed + /// even if it never runs a turn. Local sessions only: cloud creation rejects + /// it. + pub name: Option, /// Model to use (e.g. `"gpt-4"`, `"claude-sonnet-4"`). pub model: Option, /// Exact model IDs this session may use. When unset, the host imposes no @@ -2129,7 +2174,8 @@ pub struct SessionConfig { pub refresh_custom_instructions: Option, /// When true, enables on-demand instruction discovery for this session. pub enable_on_demand_instruction_discovery: Option, - /// When true, enables file hooks for this session. + /// Enables repository-origin file hooks from `.github/hooks/` and trusted + /// repository-configured roots, not explicit `hook_directories` or callback hooks. pub enable_file_hooks: Option, /// When true, allows host Git operations for this session. pub enable_host_git_operations: Option, @@ -2176,10 +2222,30 @@ pub struct SessionConfig { /// `disable_form_deferral` only applies to that server and only has an /// effect when MCP Apps and form-backed GitHub tools are enabled. pub github_mcp_tool_config: Option, - /// Skill directory paths passed through to the GitHub Copilot CLI. + /// Explicit discovery-only skill roots, not general filesystem grants. + /// Available without `enable_config_discovery`, subject to skill/managed disables. + /// Relative paths resolve once when supplied against the request's effective + /// `working_directory`; retained roots do not move on cwd changes. + /// `None` and omission are equivalent; an empty list clears only the explicit SDK contribution. pub skill_directories: Option>, - /// Additional directories to search for custom instruction files. - /// Forwarded to the CLI; not the same as [`skill_directories`](Self::skill_directories). + /// Explicit discovery-only agent roots, separate from `custom_agents` objects. + /// No general filesystem grant. Available without `enable_config_discovery`, + /// subject to source/managed disables. Relative paths resolve once when supplied + /// against the request's effective `working_directory`; retained roots do not move. + /// `None` and omission are equivalent; an empty list clears only the explicit SDK contribution. + pub agent_directories: Option>, + /// Explicit discovery-only file-hook roots, separate from callback `hooks`. + /// No general filesystem grant. Available without `enable_config_discovery` or + /// `enable_file_hooks` (repository-origin hooks only), subject to hook/managed disables. + /// Relative paths resolve once when supplied against the request's effective + /// `working_directory`; retained roots do not move on cwd changes. + /// `None` and omission are equivalent; an empty list clears only the explicit SDK contribution. + pub hook_directories: Option>, + /// Explicit discovery-only instruction roots using existing SDK formats. + /// No general filesystem grant. Available without `enable_config_discovery`, + /// subject to instruction/managed disables. Relative paths resolve once when supplied + /// against the request's effective `working_directory`; retained roots do not move. + /// `None` and omission are equivalent; an empty list clears only the explicit SDK contribution. pub instruction_directories: Option>, /// Open Plugin directory paths passed through to the CLI. pub plugin_directories: Option>, @@ -2189,6 +2255,8 @@ pub struct SessionConfig { /// rarely used tools behind a searchable index. When unset, the runtime /// default applies. pub tool_search: Option, + /// **Experimental.** Non-persisted image generation opt-in. Re-supply after runtime restart. + pub image_generation: Option, /// Skill names to disable. Skills in this set will not be available /// even if found in skill directories. pub disabled_skills: Option>, @@ -2412,6 +2480,7 @@ impl std::fmt::Debug for SessionConfig { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("SessionConfig") .field("session_id", &self.session_id) + .field("name", &self.name) .field("model", &self.model) .field("allowed_models", &self.allowed_models) .field("client_name", &self.client_name) @@ -2474,10 +2543,13 @@ impl std::fmt::Debug for SessionConfig { ) .field("enable_mcp_apps", &self.enable_mcp_apps) .field("skill_directories", &self.skill_directories) + .field("agent_directories", &self.agent_directories) + .field("hook_directories", &self.hook_directories) .field("instruction_directories", &self.instruction_directories) .field("plugin_directories", &self.plugin_directories) .field("large_output", &self.large_output) .field("tool_search", &self.tool_search) + .field("image_generation", &self.image_generation) .field("disabled_skills", &self.disabled_skills) .field("disabled_mcp_servers", &self.disabled_mcp_servers) .field("hooks", &self.hooks) @@ -2573,6 +2645,7 @@ impl Default for SessionConfig { fn default() -> Self { Self { session_id: None, + name: None, model: None, allowed_models: None, client_name: None, @@ -2612,10 +2685,13 @@ impl Default for SessionConfig { enable_mcp_apps: None, github_mcp_tool_config: None, skill_directories: None, + agent_directories: None, + hook_directories: None, instruction_directories: None, plugin_directories: None, large_output: None, tool_search: None, + image_generation: None, disabled_skills: None, disabled_mcp_servers: None, hooks: None, @@ -2739,6 +2815,7 @@ impl SessionConfig { let wire = crate::wire::SessionCreateWire { session_id, + name: self.name, model: self.model, allowed_models: self.allowed_models, client_name: self.client_name, @@ -2784,10 +2861,13 @@ impl SessionConfig { github_mcp_tool_config: self.github_mcp_tool_config, hooks: hooks_flag, skill_directories: self.skill_directories, + agent_directories: self.agent_directories, + hook_directories: self.hook_directories, instruction_directories: self.instruction_directories, plugin_directories: self.plugin_directories, large_output: self.large_output, tool_search: self.tool_search, + image_generation: self.image_generation, disabled_skills: self.disabled_skills, disabled_mcp_servers: self.disabled_mcp_servers, custom_agents: self.custom_agents, @@ -2964,6 +3044,15 @@ impl SessionConfig { self } + /// Set [`Self::name`]. The runtime rejects a name that is empty, padded with + /// whitespace, longer than 100 UTF-16 units, or contains control characters + /// or double quotes, so `create_session` fails rather than creating an + /// unnamed or unrenameable session. + pub fn with_name(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); + self + } + /// Set the model identifier (e.g. `"claude-sonnet-4"`). pub fn with_model(mut self, model: impl Into) -> Self { self.model = Some(model.into()); @@ -3234,6 +3323,26 @@ impl SessionConfig { self } + /// Set explicit discovery-only agent roots. See [`Self::agent_directories`]. + pub fn with_agent_directories(mut self, paths: I) -> Self + where + I: IntoIterator, + P: Into, + { + self.agent_directories = Some(paths.into_iter().map(Into::into).collect()); + self + } + + /// Set explicit discovery-only file-hook roots. See [`Self::hook_directories`]. + pub fn with_hook_directories(mut self, paths: I) -> Self + where + I: IntoIterator, + P: Into, + { + self.hook_directories = Some(paths.into_iter().map(Into::into).collect()); + self + } + /// Set the runtime-bundled skill allowlist. pub fn with_included_builtin_skills(mut self, names: I) -> Self where @@ -3279,6 +3388,12 @@ impl SessionConfig { self } + /// **Experimental.** Set the non-persisted image generation opt-in on create. + pub fn with_image_generation(mut self, config: ImageGenerationConfig) -> Self { + self.image_generation = Some(config); + self + } + /// Set the names of skills to disable (overrides skill discovery). pub fn with_disabled_skills(mut self, names: I) -> Self where @@ -3654,7 +3769,8 @@ pub struct ResumeSessionConfig { pub organization_custom_instructions: Option, /// When true, enables on-demand instruction discovery on resume. pub enable_on_demand_instruction_discovery: Option, - /// When true, enables file hooks on resume. + /// Enables repository-origin file hooks from `.github/hooks/` and trusted + /// repository-configured roots, not explicit `hook_directories` or callback hooks. pub enable_file_hooks: Option, /// When true, allows host Git operations on resume. pub enable_host_git_operations: Option, @@ -3678,10 +3794,30 @@ pub struct ResumeSessionConfig { /// `disable_form_deferral` only applies to that server and only has an /// effect when MCP Apps and form-backed GitHub tools are enabled. pub github_mcp_tool_config: Option, - /// Skill directory paths passed through to the GitHub Copilot CLI on resume. + /// Explicit discovery-only skill roots, not general filesystem grants. + /// Available without `enable_config_discovery`, subject to skill/managed disables. + /// Relative paths resolve once when supplied against the request's effective + /// `working_directory`; retained roots do not move on cwd changes. + /// `None` and omission are equivalent; an empty list clears only the explicit SDK contribution. pub skill_directories: Option>, - /// Additional directories to search for custom instruction files on - /// resume. Forwarded to the CLI; not the same as [`skill_directories`](Self::skill_directories). + /// Explicit discovery-only agent roots, separate from `custom_agents` objects. + /// No general filesystem grant. Available without `enable_config_discovery`, + /// subject to source/managed disables. Relative paths resolve once when supplied + /// against the request's effective `working_directory`; retained roots do not move. + /// `None` and omission are equivalent; an empty list clears only the explicit SDK contribution. + pub agent_directories: Option>, + /// Explicit discovery-only file-hook roots, separate from callback `hooks`. + /// No general filesystem grant. Available without `enable_config_discovery` or + /// `enable_file_hooks` (repository-origin hooks only), subject to hook/managed disables. + /// Relative paths resolve once when supplied against the request's effective + /// `working_directory`; retained roots do not move on cwd changes. + /// `None` and omission are equivalent; an empty list clears only the explicit SDK contribution. + pub hook_directories: Option>, + /// Explicit discovery-only instruction roots using existing SDK formats. + /// No general filesystem grant. Available without `enable_config_discovery`, + /// subject to instruction/managed disables. Relative paths resolve once when supplied + /// against the request's effective `working_directory`; retained roots do not move. + /// `None` and omission are equivalent; an empty list clears only the explicit SDK contribution. pub instruction_directories: Option>, /// Open Plugin directory paths passed through to the CLI on resume. pub plugin_directories: Option>, @@ -3690,6 +3826,8 @@ pub struct ResumeSessionConfig { /// Overrides the runtime's built-in tool-search behavior on resume. When /// unset, the runtime default applies. pub tool_search: Option, + /// **Experimental.** Non-persisted image generation opt-in. Re-supply after runtime restart. + pub image_generation: Option, /// Skill names to disable on resume. pub disabled_skills: Option>, /// Exact MCP server names to disable on resume. This prevents startup and @@ -3917,10 +4055,13 @@ impl std::fmt::Debug for ResumeSessionConfig { ) .field("enable_mcp_apps", &self.enable_mcp_apps) .field("skill_directories", &self.skill_directories) + .field("agent_directories", &self.agent_directories) + .field("hook_directories", &self.hook_directories) .field("instruction_directories", &self.instruction_directories) .field("plugin_directories", &self.plugin_directories) .field("large_output", &self.large_output) .field("tool_search", &self.tool_search) + .field("image_generation", &self.image_generation) .field("disabled_skills", &self.disabled_skills) .field("disabled_mcp_servers", &self.disabled_mcp_servers) .field("hooks", &self.hooks) @@ -4092,10 +4233,13 @@ impl ResumeSessionConfig { github_mcp_tool_config: self.github_mcp_tool_config, hooks: hooks_flag, skill_directories: self.skill_directories, + agent_directories: self.agent_directories, + hook_directories: self.hook_directories, instruction_directories: self.instruction_directories, plugin_directories: self.plugin_directories, large_output: self.large_output, tool_search: self.tool_search, + image_generation: self.image_generation, disabled_skills: self.disabled_skills, disabled_mcp_servers: self.disabled_mcp_servers, custom_agents: self.custom_agents, @@ -4201,10 +4345,13 @@ impl ResumeSessionConfig { enable_mcp_apps: None, github_mcp_tool_config: None, skill_directories: None, + agent_directories: None, + hook_directories: None, instruction_directories: None, plugin_directories: None, large_output: None, tool_search: None, + image_generation: None, disabled_skills: None, disabled_mcp_servers: None, hooks: None, @@ -4624,6 +4771,26 @@ impl ResumeSessionConfig { self } + /// Set explicit discovery-only agent roots. See [`Self::agent_directories`]. + pub fn with_agent_directories(mut self, paths: I) -> Self + where + I: IntoIterator, + P: Into, + { + self.agent_directories = Some(paths.into_iter().map(Into::into).collect()); + self + } + + /// Set explicit discovery-only file-hook roots. See [`Self::hook_directories`]. + pub fn with_hook_directories(mut self, paths: I) -> Self + where + I: IntoIterator, + P: Into, + { + self.hook_directories = Some(paths.into_iter().map(Into::into).collect()); + self + } + /// Set the runtime-bundled skill allowlist on resume. pub fn with_included_builtin_skills(mut self, names: I) -> Self where @@ -4669,6 +4836,12 @@ impl ResumeSessionConfig { self } + /// **Experimental.** Set the image generation opt-in on resume, including after runtime restart. + pub fn with_image_generation(mut self, config: ImageGenerationConfig) -> Self { + self.image_generation = Some(config); + self + } + /// Set the names of skills to disable on resume. pub fn with_disabled_skills(mut self, names: I) -> Self where @@ -6524,6 +6697,9 @@ impl Default for ExitPlanModeData { } } +#[cfg(test)] +mod customization_directory_tests; + #[cfg(test)] mod tests; diff --git a/rust/src/types/customization_directory_tests.rs b/rust/src/types/customization_directory_tests.rs new file mode 100644 index 0000000000..67a5dadf15 --- /dev/null +++ b/rust/src/types/customization_directory_tests.rs @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +#![cfg(test)] + +use std::path::PathBuf; + +use serde_json::{Value, json}; + +use super::{ResumeSessionConfig, SessionConfig, SessionId}; + +fn assert_roots(wire: &Value, roots: Option<&[PathBuf]>) { + for (key, kind) in [ + ("skillDirectories", "skills"), + ("agentDirectories", "agents"), + ("instructionDirectories", "instructions"), + ("hookDirectories", "hooks"), + ] { + match roots { + Some(paths) => assert_eq!( + wire[key], + serde_json::to_value(paths.iter().map(|path| path.join(kind)).collect::>()) + .unwrap() + ), + None => assert!(wire.get(key).is_none(), "unexpected {key}: {wire}"), + } + } + assert!(wire.get("additionalDirectories").is_none()); + assert!(wire.get("customAgents").is_none()); + assert_eq!(wire["hooks"], false); + assert_eq!(wire["enableFileHooks"], false); + assert_eq!(wire["enableConfigDiscovery"], false); +} + +#[test] +fn create_and_resume_preserve_discovery_roots_without_client_rebasing() { + for roots in [ + None, + Some(Vec::new()), + Some(vec![ + PathBuf::from("relative-root"), + PathBuf::from("other-root"), + ]), + ] { + let mut create = SessionConfig::default() + .with_working_directory("request-cwd") + .with_enable_config_discovery(false) + .with_enable_file_hooks(false); + let mut resume = ResumeSessionConfig::new(SessionId::from("resumed")) + .with_working_directory("resume-cwd") + .with_enable_config_discovery(false) + .with_enable_file_hooks(false); + if let Some(paths) = &roots { + create = create + .with_skill_directories(paths.iter().map(|path| path.join("skills"))) + .with_agent_directories(paths.iter().map(|path| path.join("agents"))) + .with_instruction_directories(paths.iter().map(|path| path.join("instructions"))) + .with_hook_directories(paths.iter().map(|path| path.join("hooks"))); + resume = resume + .with_skill_directories(paths.iter().map(|path| path.join("skills"))) + .with_agent_directories(paths.iter().map(|path| path.join("agents"))) + .with_instruction_directories(paths.iter().map(|path| path.join("instructions"))) + .with_hook_directories(paths.iter().map(|path| path.join("hooks"))); + } + let (wire, _) = create.into_wire(None).unwrap(); + assert_roots(&serde_json::to_value(wire).unwrap(), roots.as_deref()); + let (wire, _) = resume.into_wire().unwrap(); + assert_roots(&serde_json::to_value(wire).unwrap(), roots.as_deref()); + } +} + +#[test] +fn host_factory_forwards_agent_and_hook_roots() { + let create = SessionConfig::default() + .with_agent_directories(["relative-agents"]) + .with_hook_directories(Vec::::new()); + let resume = ResumeSessionConfig::new(SessionId::from("resumed")) + .with_agent_directories(["relative-agents"]) + .with_hook_directories(Vec::::new()); + for wire in [ + crate::ahp_host::config_for_host(&create).unwrap(), + crate::ahp_host::resume_config_for_host(&resume).unwrap(), + ] { + assert_eq!(wire["agentDirectories"], json!(["relative-agents"])); + assert_eq!(wire["hookDirectories"], json!([])); + assert!(wire.get("customAgents").is_none()); + assert!(wire.get("hooks").is_none()); + } +} diff --git a/rust/src/types/tests.rs b/rust/src/types/tests.rs index 774feeda9f..451fe35191 100644 --- a/rust/src/types/tests.rs +++ b/rust/src/types/tests.rs @@ -13,11 +13,11 @@ use super::{ AgentMode, Attachment, AttachmentLineRange, AttachmentSelectionPosition, AttachmentSelectionRange, AutoTier, AzureProviderOptions, CapiSessionOptions, ConnectionState, CopilotExpAssignmentResponse, CustomAgentConfig, DeliveryMode, ExpConfigEntry, ExpFlagValue, - ExtensionInfo, GitHubMcpToolConfig, GitHubReferenceType, InfiniteSessionConfig, - LargeToolOutputConfig, McpServerConfig, McpStdioServerConfig, MemoryConfiguration, - NamedProviderConfig, PermissionResponseCapability, ProviderConfig, ProviderModelConfig, - ReasoningSummary, ResumeSessionConfig, SessionConfig, SessionEvent, SessionId, - SystemMessageConfig, Tool, ToolBinaryResult, ToolResult, ToolResultExpanded, + ExtensionInfo, GitHubMcpToolConfig, GitHubReferenceType, ImageGenerationConfig, + InfiniteSessionConfig, LargeToolOutputConfig, McpServerConfig, McpStdioServerConfig, + MemoryConfiguration, NamedProviderConfig, PermissionResponseCapability, ProviderConfig, + ProviderModelConfig, ReasoningSummary, ResumeSessionConfig, SessionConfig, SessionEvent, + SessionId, SystemMessageConfig, Tool, ToolBinaryResult, ToolResult, ToolResultExpanded, ToolResultResponse, ensure_attachment_display_names, }; use crate::generated::session_events::TypedSessionEvent; @@ -135,6 +135,55 @@ fn custom_agent_config_omits_reasoning_effort_when_none() { assert!(wire.get("reasoningEffort").is_none()); } +#[test] +fn custom_agent_config_hooks_round_trip() { + use indexmap::IndexMap; + + let action: IndexMap = IndexMap::from([ + ("type".into(), json!("command")), + ("bash".into(), json!("echo scoped-hook")), + ("timeoutSec".into(), json!(10)), + ]); + let hooks = IndexMap::from([("preToolUse".into(), vec![action])]); + let agent = CustomAgentConfig::new("hook-agent", "prompt").with_hooks(hooks.clone()); + assert_eq!(agent.hooks.as_ref(), Some(&hooks)); + + let wire = serde_json::to_value(&agent).unwrap(); + assert_eq!( + wire["hooks"], + json!({"preToolUse": [{"type": "command", "bash": "echo scoped-hook", "timeoutSec": 10}]}) + ); + let restored: CustomAgentConfig = serde_json::from_value(wire).unwrap(); + assert_eq!(restored.hooks, Some(hooks)); +} + +#[test] +fn custom_agent_config_hooks_reject_non_object_actions() { + for action in [ + json!(null), + json!(true), + json!(42), + json!("command"), + json!([]), + ] { + let result = serde_json::from_value::(json!({ + "name": "invalid-hook-agent", + "prompt": "prompt", + "hooks": {"preToolUse": [action]} + })); + assert!( + result.is_err(), + "expected non-object hook action {action} to fail, got {result:?}" + ); + } +} + +#[test] +fn custom_agent_config_omits_hooks_when_unset() { + let wire = serde_json::to_value(CustomAgentConfig::new("plain-agent", "prompt")).unwrap(); + assert!(wire.get("hooks").is_none()); +} + #[test] #[should_panic(expected = "tool parameter schema must be a JSON object")] fn tool_with_parameters_panics_on_non_object_value() { @@ -1947,3 +1996,40 @@ fn github_anchored_attachment_variants_round_trip() { ); } } +#[test] +fn image_generation_create_and_resume_wire_options() { + for enabled in [true, false] { + let config = ImageGenerationConfig::new().with_enabled(enabled); + let (create, _) = SessionConfig::default() + .with_image_generation(config.clone()) + .into_wire(Some(SessionId::from("image-test"))) + .unwrap(); + let (resume, _) = ResumeSessionConfig::new(SessionId::from("image-test")) + .with_image_generation(config) + .into_wire() + .unwrap(); + for wire in [ + serde_json::to_value(create).unwrap(), + serde_json::to_value(resume).unwrap(), + ] { + assert_eq!( + wire["imageGeneration"], + serde_json::json!({"enabled": enabled}) + ); + } + } + let (create, _) = SessionConfig::default().into_wire(None).unwrap(); + let (resume, _) = ResumeSessionConfig::new(SessionId::from("image-test")) + .into_wire() + .unwrap(); + for wire in [ + serde_json::to_value(create).unwrap(), + serde_json::to_value(resume).unwrap(), + ] { + assert!(wire.get("imageGeneration").is_none(), "{wire}"); + } + assert_eq!( + serde_json::to_value(ImageGenerationConfig::new()).unwrap(), + serde_json::json!({}) + ); +} diff --git a/rust/src/wire.rs b/rust/src/wire.rs index 2720d3bf2d..7c5e2ccbd4 100644 --- a/rust/src/wire.rs +++ b/rust/src/wire.rs @@ -27,9 +27,9 @@ use crate::generated::session_events::ReasoningSummary; use crate::types::{ AskUserVariant, CanvasProviderIdentity, CapiSessionOptions, CloudSessionOptions, CustomAgentConfig, DefaultAgentConfig, ExtensionInfo, GitHubMcpToolConfig, - InfiniteSessionConfig, LargeToolOutputConfig, McpServerConfig, MemoryConfiguration, - NamedProviderConfig, ProviderConfig, ProviderModelConfig, SessionId, SessionLimitsConfig, - SystemMessageConfig, Tool, ToolSearchConfig, + ImageGenerationConfig, InfiniteSessionConfig, LargeToolOutputConfig, McpServerConfig, + MemoryConfiguration, NamedProviderConfig, ProviderConfig, ProviderModelConfig, SessionId, + SessionLimitsConfig, SystemMessageConfig, Tool, ToolSearchConfig, }; /// Wire representation of a slash command (name + description only). The @@ -51,6 +51,8 @@ pub(crate) struct SessionCreateWire { #[serde(skip_serializing_if = "Option::is_none")] pub session_id: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub model: Option, #[serde(skip_serializing_if = "Option::is_none")] pub allowed_models: Option>, @@ -137,6 +139,10 @@ pub(crate) struct SessionCreateWire { #[serde(skip_serializing_if = "Option::is_none")] pub skill_directories: Option>, #[serde(skip_serializing_if = "Option::is_none")] + pub agent_directories: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub hook_directories: Option>, + #[serde(skip_serializing_if = "Option::is_none")] pub instruction_directories: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub plugin_directories: Option>, @@ -145,6 +151,8 @@ pub(crate) struct SessionCreateWire { #[serde(skip_serializing_if = "Option::is_none")] pub tool_search: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub image_generation: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub disabled_skills: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub disabled_mcp_servers: Option>, @@ -309,6 +317,10 @@ pub(crate) struct SessionResumeWire { #[serde(skip_serializing_if = "Option::is_none")] pub skill_directories: Option>, #[serde(skip_serializing_if = "Option::is_none")] + pub agent_directories: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub hook_directories: Option>, + #[serde(skip_serializing_if = "Option::is_none")] pub instruction_directories: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub plugin_directories: Option>, @@ -317,6 +329,8 @@ pub(crate) struct SessionResumeWire { #[serde(skip_serializing_if = "Option::is_none")] pub tool_search: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub image_generation: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub disabled_skills: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub disabled_mcp_servers: Option>, diff --git a/rust/tests/api_types_test.rs b/rust/tests/api_types_test.rs index 2941c8f692..d8b705f075 100644 --- a/rust/tests/api_types_test.rs +++ b/rust/tests/api_types_test.rs @@ -11,6 +11,7 @@ use github_copilot_sdk::rpc::{ CustomizationReloadOutcome, CustomizationReloadStatus, CustomizationReloadSubsystem, EnqueueCommandResult, Extension, ExtensionList, ExtensionSource, ExtensionStatus, ExtensionsDisableRequest, ExtensionsEnableRequest, FleetStartRequest, FleetStartResult, + ManagedPermissionEvaluation, ManagedPermissionOperationKind, ManagedPermissionsEvaluateRequest, McpConfiguredServer, McpConfiguredServerList, McpConfiguredServerState, McpDisableRequest, McpEnableOptions, McpEnableRequest, McpInstallationOperationStatus, McpOauthLoginOptions, McpOauthLoginRequest, McpServer, McpStopServerRequest, ModelSetAllowedModelsRequest, @@ -25,6 +26,30 @@ use github_copilot_sdk::session_events::{ }; use github_copilot_sdk::{AutoTier, AutoTierPreference, SetModelOptions}; +#[test] +fn managed_permission_results_reuse_operations_without_reconstruction() { + let evaluation: ManagedPermissionEvaluation = serde_json::from_value(serde_json::json!({ + "operation": {"kind": "url", "url": "https://example.com/a"}, + "verdict": "allow", + })) + .unwrap(); + let request = ManagedPermissionsEvaluateRequest { + context: serde_json::from_value(serde_json::json!({"failClosed": false})).unwrap(), + operations: vec![evaluation.operation], + }; + assert_eq!( + request.operations[0].kind, + ManagedPermissionOperationKind::Url + ); + assert_eq!( + serde_json::to_value(request).unwrap(), + serde_json::json!({ + "context": {"failClosed": false}, + "operations": [{"kind": "url", "url": "https://example.com/a"}], + }) + ); +} + #[test] fn extensible_tier_unknown_literal_retains_its_provider_identity() { let wire = serde_json::json!("Unknown"); diff --git a/rust/tests/e2e/client_options.rs b/rust/tests/e2e/client_options.rs index c516f84be6..563ae1328b 100644 --- a/rust/tests/e2e/client_options.rs +++ b/rust/tests/e2e/client_options.rs @@ -18,6 +18,10 @@ use serde::Deserialize; use serde_json::{Value, json}; use tempfile::TempDir; +#[cfg(windows)] +#[path = "client_options/windows_process_tree.rs"] +mod windows_process_tree; + #[tokio::test] async fn should_forward_advanced_session_creation_options_to_the_cli() { let fake = FakeCli::new(); @@ -32,6 +36,7 @@ async fn should_forward_advanced_session_creation_options_to_the_cli() { .create_session( SessionConfig::default() .with_session_id("advanced-session-id") + .with_name("advanced session") .with_client_name("rust-sdk-e2e-client") .with_model("claude-sonnet-5") .with_reasoning_effort("low") @@ -95,6 +100,7 @@ async fn should_forward_advanced_session_creation_options_to_the_cli() { params, [ ("sessionId", json!("advanced-session-id")), + ("name", json!("advanced session")), ("clientName", json!("rust-sdk-e2e-client")), ("model", json!("claude-sonnet-5")), ("reasoningEffort", json!("low")), diff --git a/rust/tests/e2e/client_options/windows_process_tree.rs b/rust/tests/e2e/client_options/windows_process_tree.rs new file mode 100644 index 0000000000..36035536ac --- /dev/null +++ b/rust/tests/e2e/client_options/windows_process_tree.rs @@ -0,0 +1,252 @@ +#![cfg(windows)] + +use std::io; +use std::os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle}; +use std::path::Path; +use std::time::Duration; + +use github_copilot_sdk::{CliProgram, Client, ClientOptions, Transport}; +use windows_sys::Win32::System::Threading::{ + OpenProcess, PROCESS_SYNCHRONIZE, PROCESS_TERMINATE, TerminateProcess, WaitForSingleObject, +}; + +use super::ShutdownCli; + +struct FixtureProcess(OwnedHandle); + +impl FixtureProcess { + fn open(pid: u32) -> Self { + // SAFETY: this PID was published by this test's owned fixture. + let handle = unsafe { OpenProcess(PROCESS_SYNCHRONIZE | PROCESS_TERMINATE, 0, pid) }; + assert!(!handle.is_null(), "{}", io::Error::last_os_error()); + // SAFETY: OpenProcess returned a new independently owned handle. + Self(unsafe { OwnedHandle::from_raw_handle(handle) }) + } + + fn alive(&self) -> bool { + // SAFETY: the retained process handle is valid even after the process exits. + match unsafe { WaitForSingleObject(self.0.as_raw_handle().cast(), 0) } { + 0 => false, + 258 => true, + result => panic!( + "process wait failed ({result}): {}", + io::Error::last_os_error() + ), + } + } + + fn terminate(&self) { + // SAFETY: this handle uniquely identifies a fixture-owned process, not a reused PID. + assert_ne!( + unsafe { TerminateProcess(self.0.as_raw_handle().cast(), 1) }, + 0, + "{}", + io::Error::last_os_error() + ); + } + + async fn assert_exited(&self) { + tokio::time::timeout(Duration::from_secs(5), async { + while self.alive() { + tokio::time::sleep(Duration::from_millis(10)).await; + } + }) + .await + .expect("owned root and descendant must terminate"); + } +} + +impl Drop for FixtureProcess { + fn drop(&mut self) { + // SAFETY: fallback cleanup targets only this fixture's retained process handle. + unsafe { + if WaitForSingleObject(self.0.as_raw_handle().cast(), 0) == 258 { + if TerminateProcess(self.0.as_raw_handle().cast(), 1) == 0 { + eprintln!( + "fixture fallback termination failed: {}", + io::Error::last_os_error() + ); + } + if WaitForSingleObject(self.0.as_raw_handle().cast(), 5000) != 0 { + eprintln!( + "fixture fallback reap failed: {}", + io::Error::last_os_error() + ); + } + } + } + } +} + +fn options(fake: &ShutdownCli, tcp: bool) -> ClientOptions { + let mut options = fake.options(); + options.prefix_args.push("--fixture-descendant".into()); + if tcp { + options.prefix_args.push("--fixture-tcp".into()); + options.transport = Transport::Tcp { + port: 0, + connection_token: None, + }; + } + options +} + +async fn published_pid(path: &Path) -> u32 { + tokio::time::timeout(Duration::from_secs(10), async { + loop { + match std::fs::read_to_string(path) { + Ok(value) => { + if let Ok(pid) = value.trim().parse() { + return pid; + } + } + Err(error) if error.kind() == io::ErrorKind::NotFound => {} + Err(error) => panic!("read fixture PID: {error}"), + } + tokio::time::sleep(Duration::from_millis(10)).await; + } + }) + .await + .expect("fixture must publish its PID") +} + +async fn retain_tree(fake: &ShutdownCli) -> (FixtureProcess, FixtureProcess) { + let root = FixtureProcess::open(published_pid(&fake.pid_file).await); + let descendant_path = fake.pid_file.with_file_name("child.pid.descendant"); + let descendant = FixtureProcess::open(published_pid(&descendant_path).await); + assert!(root.alive()); + assert!(descendant.alive()); + (root, descendant) +} + +#[tokio::test] +async fn stop_force_stop_and_drop_terminate_owned_descendants() { + for tcp in [false, true] { + for action in ["stop", "force_stop", "drop"] { + let fake = ShutdownCli::new("force"); + let client = Client::start(options(&fake, tcp)) + .await + .expect("start fixture CLI"); + let (root, descendant) = retain_tree(&fake).await; + match action { + "stop" => client.stop().await.expect("stop fixture CLI"), + "force_stop" => client.force_stop(), + "drop" => {} + _ => unreachable!(), + } + drop(client); + root.assert_exited().await; + descendant.assert_exited().await; + } + } +} + +#[tokio::test] +async fn cancelled_startup_terminates_root_and_descendant() { + for (tcp, mode) in [ + (false, "connect-stall"), + (true, "connect-stall"), + (true, "port-stall"), + ] { + let fake = ShutdownCli::new(mode); + let starting = tokio::spawn(Client::start(options(&fake, tcp))); + let (root, descendant) = retain_tree(&fake).await; + starting.abort(); + assert!(starting.await.expect_err("cancel startup").is_cancelled()); + root.assert_exited().await; + descendant.assert_exited().await; + } +} + +#[tokio::test] +async fn startup_process_exit_terminates_descendant() { + for tcp in [false, true] { + let fake = ShutdownCli::new("connect-stall"); + let starting = tokio::spawn(Client::start(options(&fake, tcp))); + let (root, descendant) = retain_tree(&fake).await; + root.terminate(); + let result = tokio::time::timeout(Duration::from_secs(5), starting) + .await + .expect("startup failure must be bounded") + .expect("join startup task"); + assert!(result.is_err()); + root.assert_exited().await; + descendant.assert_exited().await; + } +} + +#[tokio::test] +async fn last_clone_drop_is_private_to_its_client_job() { + for tcp in [false, true] { + let first = ShutdownCli::new("force"); + let client = Client::start(options(&first, tcp)) + .await + .expect("start first client"); + let retained_client = client.clone(); + let first_tree = retain_tree(&first).await; + let second = ShutdownCli::new("force"); + let second_client = Client::start(options(&second, tcp)) + .await + .expect("start second client"); + let second_tree = retain_tree(&second).await; + + drop(client); + assert!(first_tree.0.alive()); + assert!(first_tree.1.alive()); + drop(retained_client); + first_tree.0.assert_exited().await; + first_tree.1.assert_exited().await; + assert!(second_tree.0.alive()); + assert!(second_tree.1.alive()); + second_client + .ping(Some("private-job-isolation")) + .await + .expect("other client's Job must remain alive"); + second_client.force_stop(); + second_tree.0.assert_exited().await; + second_tree.1.assert_exited().await; + } +} + +#[tokio::test] +async fn abrupt_host_exit_terminates_root_and_descendant_without_rust_cleanup() { + for tcp in [false, true] { + let fake = ShutdownCli::new("force"); + let options = options(&fake, tcp); + let CliProgram::Path(program) = options.program else { + panic!("fixture uses an explicit program"); + }; + let prefix: Vec<_> = options + .prefix_args + .iter() + .map(|arg| arg.to_str().expect("fixture argument is UTF-8")) + .collect(); + let host_pid_file = fake.dir.path().join("host-cli.pid"); + let mut host = + tokio::process::Command::new(env!("CARGO_BIN_EXE_copilot-host-crash-fixture")) + .env("HOST_CRASH_FIXTURE_PROGRAM", program) + .env( + "HOST_CRASH_FIXTURE_PREFIX_ARGS_JSON", + serde_json::to_string(&prefix).expect("fixture args"), + ) + .env("HOST_CRASH_FIXTURE_CWD", fake.dir.path()) + .env("HOST_CRASH_FIXTURE_ENV_JSON", "[]") + .env("HOST_CRASH_FIXTURE_PID_FILE", &host_pid_file) + .env( + "HOST_CRASH_FIXTURE_TRANSPORT", + if tcp { "tcp" } else { "stdio" }, + ) + .kill_on_drop(true) + .spawn() + .expect("spawn SDK host fixture"); + let reported_root = published_pid(&host_pid_file).await; + assert_eq!(reported_root, published_pid(&fake.pid_file).await); + let (root, descendant) = retain_tree(&fake).await; + tokio::time::timeout(Duration::from_secs(5), host.kill()) + .await + .expect("bound abrupt host termination") + .expect("kill and reap SDK host"); + root.assert_exited().await; + descendant.assert_exited().await; + } +} diff --git a/rust/tests/e2e/session_lifecycle.rs b/rust/tests/e2e/session_lifecycle.rs index 545bb49885..d406297e02 100644 --- a/rust/tests/e2e/session_lifecycle.rs +++ b/rust/tests/e2e/session_lifecycle.rs @@ -1,7 +1,12 @@ +use std::sync::Arc; + +use github_copilot_sdk::ResumeSessionConfig; +use github_copilot_sdk::handler::ApproveAllHandler; use github_copilot_sdk::session_events::SessionEventType; use super::support::{ - assistant_message_content, collect_until_idle, event_types, wait_for_condition, + DEFAULT_TEST_TOKEN, assistant_message_content, collect_until_idle, event_types, + wait_for_condition, }; #[tokio::test] @@ -57,6 +62,75 @@ async fn should_list_created_sessions_after_sending_a_message() { .await; } +#[tokio::test] +async fn should_persist_a_session_named_at_create_without_a_turn() { + // Dedicated context: the persisted name must survive a real CLI restart, and the + // shared group's client is never actually stopped. + super::support::with_dedicated_e2e_context( + "session_lifecycle", + "should_persist_a_session_named_at_create_without_a_turn", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let named = client + .create_session( + ctx.approve_all_session_config() + .with_name("Named at create"), + ) + .await + .expect("create named session"); + let named_id = named.id().clone(); + let unnamed = client + .create_session(ctx.approve_all_session_config()) + .await + .expect("create unnamed session"); + let unnamed_id = unnamed.id().clone(); + + let created_name = named.rpc().name().get().await.expect("get name").name; + assert_eq!(created_name.as_deref(), Some("Named at create")); + + named.disconnect().await.expect("disconnect named session"); + unnamed + .disconnect() + .await + .expect("disconnect unnamed session"); + + // Neither session ran a turn: only the create-time name makes one persist. + let listed: std::collections::HashSet<_> = client + .list_sessions(None) + .await + .expect("list sessions") + .into_iter() + .map(|session| session.session_id) + .collect(); + assert!(listed.contains(&named_id), "named session is listed"); + assert!(!listed.contains(&unnamed_id), "unnamed session is not"); + + // Restart the CLI so the resume below reads the name back from disk. + client.stop().await.expect("stop client"); + let cold_client = ctx.start_client().await; + let resumed = cold_client + .resume_session( + ResumeSessionConfig::new(named_id.clone()) + .with_permission_handler(Arc::new(ApproveAllHandler)) + .with_github_token(DEFAULT_TEST_TOKEN), + ) + .await + .expect("resume named session"); + let read = resumed.rpc().name().get().await.expect("get resumed name"); + assert_eq!(read.name.as_deref(), Some("Named at create")); + resumed + .disconnect() + .await + .expect("disconnect resumed session"); + cold_client.stop().await.expect("stop cold client"); + }) + }, + ) + .await; +} + #[tokio::test] async fn should_delete_session_permanently() { super::support::with_shared_e2e_context( diff --git a/rust/tests/e2e/telemetry.rs b/rust/tests/e2e/telemetry.rs index fe83a1e2ea..0d54f31868 100644 --- a/rust/tests/e2e/telemetry.rs +++ b/rust/tests/e2e/telemetry.rs @@ -1,3 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + use std::sync::Arc; use async_trait::async_trait; @@ -194,12 +196,19 @@ async fn should_export_per_request_subagent_chat_spans() { .iter() .filter(|entry| string_property(entry, "type") == Some("span")) .collect(); - assert!(spans.iter().all(|span| { - span.get("instrumentationScope") - .and_then(|scope| string_property(scope, "name")) - == Some(source_name) - && status_code(span) != Some(2) - })); + for span in &spans { + assert_eq!( + span.get("instrumentationScope") + .and_then(|scope| string_property(scope, "name")), + Some(source_name), + "unexpected instrumentation scope: {span:#}" + ); + assert_ne!( + status_code(span), + Some(2), + "unexpected error span: {span:#}" + ); + } let invocations: Vec<_> = spans .iter() .copied() diff --git a/rust/tests/fixtures/host_crash_fixture.rs b/rust/tests/fixtures/host_crash_fixture.rs index 8c48379001..204b5dbb67 100644 --- a/rust/tests/fixtures/host_crash_fixture.rs +++ b/rust/tests/fixtures/host_crash_fixture.rs @@ -13,6 +13,7 @@ //! on the spawned CLI's environment. //! - `HOST_CRASH_FIXTURE_PID_FILE`: path this process atomically publishes the //! CLI child's OS process id to, once the client finishes starting. +//! - `HOST_CRASH_FIXTURE_TRANSPORT`: optional `stdio` (default) or `tcp`. use std::io::Write; use std::path::PathBuf; @@ -38,13 +39,21 @@ async fn main() { std::env::var("HOST_CRASH_FIXTURE_PID_FILE").expect("HOST_CRASH_FIXTURE_PID_FILE"), ); + let transport = match std::env::var("HOST_CRASH_FIXTURE_TRANSPORT").as_deref() { + Ok("tcp") => Transport::Tcp { + port: 0, + connection_token: None, + }, + Ok("stdio") | Err(std::env::VarError::NotPresent) => Transport::Stdio, + value => panic!("invalid HOST_CRASH_FIXTURE_TRANSPORT: {value:?}"), + }; let options = ClientOptions::new() .with_program(CliProgram::Path(PathBuf::from(program))) .with_prefix_args(prefix_args) .with_cwd(PathBuf::from(cwd)) .with_env(env_pairs) .with_use_logged_in_user(false) - .with_transport(Transport::Stdio); + .with_transport(transport); let client = Client::start(options).await.expect("start CLI client"); let pid = client.pid().expect("client reports spawned CLI pid"); diff --git a/rust/tests/http_request_body_test.rs b/rust/tests/http_request_body_test.rs new file mode 100644 index 0000000000..d200803d63 --- /dev/null +++ b/rust/tests/http_request_body_test.rs @@ -0,0 +1,614 @@ +//! The LLM-callback request body on its way through the dispatcher: the runtime sends an HTTP +//! request body in `llmInference.httpRequestChunk` frames, and the dispatcher moves the body out +//! of the parsed frame instead of copying it (a deep clone of the params `Value`, a clone of +//! `data`, and a copy of the first frame into an empty buffer). The handler must see the +//! runtime's bytes exactly: an escaped JSON body, an empty body, a body in several chunks, a +//! base64 chunk, a chunk that races ahead of its start frame, and a cancel chunk after part of +//! the body. The wire around the body is pinned too: every case asserts the exact ack of each +//! request it sends and the response frames in order, and one more case the exact error reply +//! to params that do not parse. And once the frame is parsed, a single-chunk body must not be +//! copied again: a counting allocator counts the allocations of exactly the body's length +//! between the chunk frame's write and the handler's entry. +//! +//! Each case drives the real dispatcher: `Client::start` over loopback TCP with a request +//! handler, the test playing the CLI. The allocator is process-global and `cargo test` runs a +//! binary's tests on parallel threads, so the counting case is `#[serial]` and every other case +//! `#[parallel]`: nothing else runs while it counts. + +use std::alloc::{GlobalAlloc, Layout, System}; +use std::collections::HashMap; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::time::Duration; + +use async_trait::async_trait; +use bytes::Bytes; +use futures_util::stream; +use github_copilot_sdk::{ + Client, ClientOptions, CopilotHttpRequest, CopilotHttpResponse, CopilotRequestContext, + CopilotRequestError, CopilotRequestHandler, SDK_PROTOCOL_VERSION, Transport, +}; +use http::HeaderMap; +use serde_json::{Value, json}; +use serial_test::{parallel, serial}; +use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader}; +use tokio::net::TcpListener; +use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf}; +use tokio::sync::mpsc; +use tokio::time::timeout; + +/// A backstop against a hang, never a measurement. +const DEADLINE: Duration = Duration::from_secs(30); +/// The single-chunk body the allocation count watches. Odd, so no buffer that grows by doubling +/// (serde_json's unescape scratch) lands on it, and larger than every buffer between the socket +/// and the dispatcher. +const WATCHED_BODY_BYTES: usize = 2 * 1024 * 1024 + 1; + +/// The system allocator, counting the allocations (and reallocations) of exactly [`WATCHED`] +/// bytes while it is non-zero. +struct ExactSizeCounting; + +static WATCHED: AtomicUsize = AtomicUsize::new(0); +static HITS: AtomicUsize = AtomicUsize::new(0); + +fn note(size: usize) { + let watched = WATCHED.load(Ordering::SeqCst); + if watched != 0 && size == watched { + HITS.fetch_add(1, Ordering::SeqCst); + } +} + +// SAFETY: every method forwards to `System` with the caller's own arguments, so `System`'s +// contract is the caller's; the counters are plain atomics and never touch the memory. +unsafe impl GlobalAlloc for ExactSizeCounting { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + note(layout.size()); + // SAFETY: forwarded unchanged (see the impl's SAFETY note). + unsafe { System.alloc(layout) } + } + + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + note(layout.size()); + // SAFETY: forwarded unchanged (see the impl's SAFETY note). + unsafe { System.alloc_zeroed(layout) } + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + // SAFETY: forwarded unchanged (see the impl's SAFETY note). + unsafe { System.dealloc(ptr, layout) } + } + + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + note(new_size); + // SAFETY: forwarded unchanged (see the impl's SAFETY note). + unsafe { System.realloc(ptr, layout, new_size) } + } +} + +#[global_allocator] +static ALLOCATOR: ExactSizeCounting = ExactSizeCounting; + +/// What the handler saw: the body, and the watched-size allocations counted when it was entered. +struct Captured { + body: Vec, + hits_at_entry: usize, +} + +/// What the SDK wrote while one exchange ran: its replies to the test's requests, by id, and the +/// response requests it sent (method and params), in order. +struct Wire { + replies: HashMap, + response_frames: Vec<(String, Value)>, +} + +/// The wire of a well-formed exchange: the empty-result ack of every request the test sent, and +/// nothing else, then the response head (200, no headers) and its terminal empty chunk. +fn assert_wire(wire: &Wire, request_id: &str, request_ids: &[u64]) { + for id in request_ids { + assert_eq!( + wire.replies[id], + json!({"jsonrpc": "2.0", "id": id, "result": {}}), + "the ack of request {id}" + ); + } + assert_eq!( + wire.replies.len(), + request_ids.len(), + "replies: {:?}", + wire.replies + ); + assert_eq!( + wire.response_frames, + vec![ + ( + "llmInference.httpResponseStart".to_owned(), + json!({"requestId": request_id, "status": 200, "headers": {}}), + ), + ( + "llmInference.httpResponseChunk".to_owned(), + json!({"requestId": request_id, "data": "", "end": true}), + ), + ] + ); +} + +/// Hands every request's body to the test and answers an empty 200. +struct CaptureHandler(mpsc::UnboundedSender); + +#[async_trait] +impl CopilotRequestHandler for CaptureHandler { + async fn send_request( + &self, + request: CopilotHttpRequest, + _ctx: &CopilotRequestContext, + ) -> Result { + let hits_at_entry = HITS.load(Ordering::SeqCst); + let _ = self.0.send(Captured { + body: request.body, + hits_at_entry, + }); + Ok(CopilotHttpResponse::new( + 200, + None, + HeaderMap::new(), + Box::pin(stream::empty::>()), + )) + } +} + +/// One frame in the SDK's JSON-RPC shape: a `Content-Length` header, a blank line, the body. +fn frame_bytes(message: &Value) -> Vec { + let body = serde_json::to_vec(message).expect("a JSON-RPC message serialises"); + let mut frame = format!("Content-Length: {}\r\n\r\n", body.len()).into_bytes(); + frame.extend_from_slice(&body); + frame +} + +/// The next frame the SDK wrote, decoded. +async fn read_frame(read: &mut BufReader) -> Value { + let mut length = None; + loop { + let mut line = String::new(); + let read_bytes = read.read_line(&mut line).await.expect("a header line"); + assert_ne!(read_bytes, 0, "the SDK closed the socket"); + if line == "\r\n" { + break; + } + if let Some(value) = line.strip_prefix("Content-Length:") { + length = Some(value.trim().parse::().expect("a decimal length")); + } + } + let mut body = vec![0; length.expect("a Content-Length header")]; + read.read_exact(&mut body).await.expect("the frame body"); + serde_json::from_slice(&body).expect("a JSON frame body") +} + +/// The CLI's side of one SDK client: the public `Client` over real loopback TCP, with the +/// request handler registered through `llmInference.setProvider`. +struct Peer { + client: Client, + read: BufReader, + write: OwnedWriteHalf, + captured: mpsc::UnboundedReceiver, + _work: tempfile::TempDir, +} + +impl Peer { + async fn start() -> Self { + let listener = TcpListener::bind("127.0.0.1:0") + .await + .expect("a loopback listener"); + let port = listener.local_addr().expect("a bound address").port(); + let work = tempfile::tempdir().expect("a temp dir"); + let (captured_tx, captured) = mpsc::unbounded_channel(); + let options = ClientOptions::new() + .with_program(std::env::current_exe().expect("the test binary's path")) + .with_cwd(work.path()) + .with_transport(Transport::External { + host: "127.0.0.1".into(), + port, + connection_token: None, + }) + .with_request_handler(CaptureHandler(captured_tx)); + let (client, (read, write)) = timeout(DEADLINE, async { + tokio::join!(Client::start(options), async { + let (socket, _) = listener.accept().await.expect("the SDK connects"); + socket.set_nodelay(true).expect("TCP_NODELAY"); + let (read, mut write) = socket.into_split(); + let mut read = BufReader::new(read); + for method in ["connect", "llmInference.setProvider"] { + let message = read_frame(&mut read).await; + assert_eq!(message["method"], method); + let result = if method == "connect" { + json!({ + "ok": true, + "version": "test", + "protocolVersion": SDK_PROTOCOL_VERSION + }) + } else { + json!({"success": true}) + }; + let reply = json!({"jsonrpc": "2.0", "id": message["id"], "result": result}); + write + .write_all(&frame_bytes(&reply)) + .await + .expect("the handshake reply"); + } + (read, write) + }) + }) + .await + .expect("the client handshake finishes"); + Self { + client: client.expect("the client starts"), + read, + write, + captured, + _work: work, + } + } + + /// Bounded: an SDK that stops reading with the socket open would otherwise park a large frame's + /// write on TCP backpressure, past every receive deadline. + async fn send(&mut self, message: &Value) { + timeout(DEADLINE, self.write.write_all(&frame_bytes(message))) + .await + .expect("the SDK takes the frame in time") + .expect("a frame to the SDK"); + } + + async fn start_request(&mut self, request_id: &str, id: u64) { + self.send(&json!({ + "jsonrpc": "2.0", + "id": id, + "method": "llmInference.httpRequestStart", + "params": { + "requestId": request_id, + "method": "POST", + "url": "http://unused.test/chat", + "headers": {} + } + })) + .await; + } + + async fn chunk(&mut self, id: u64, params: Value) { + self.send(&json!({ + "jsonrpc": "2.0", + "id": id, + "method": "llmInference.httpRequestChunk", + "params": params + })) + .await; + } + + /// What the handler received for the next request. + async fn captured(&mut self) -> Captured { + timeout(DEADLINE, self.captured.recv()) + .await + .expect("the handler is entered") + .expect("the handler's channel is open") + } + + /// Serves the CLI's side of the response (acking the SDK's head and body requests) until its + /// terminal body chunk has arrived and every one of `request_ids` has its reply, so the + /// exchange finishes before the client stops; returns everything the SDK wrote meanwhile. + async fn finish_response(&mut self, request_ids: &[u64]) -> Wire { + let mut wire = Wire { + replies: HashMap::new(), + response_frames: Vec::new(), + }; + timeout(DEADLINE, async { + let mut terminal = false; + while !terminal || request_ids.iter().any(|id| !wire.replies.contains_key(id)) { + let message = read_frame(&mut self.read).await; + let Some(method) = message.get("method").and_then(Value::as_str) else { + let id = message["id"] + .as_u64() + .expect("a reply carries its request's id"); + wire.replies.insert(id, message); + continue; + }; + terminal = + method == "llmInference.httpResponseChunk" && message["params"]["end"] == true; + wire.response_frames + .push((method.to_owned(), message["params"].clone())); + let ack = json!({ + "jsonrpc": "2.0", + "id": message["id"], + "result": {"accepted": true} + }); + self.send(&ack).await; + } + }) + .await + .expect("the response finishes and every request is answered"); + wire + } + + /// The SDK's replies to `request_ids`, for frames that start no exchange: any request from the + /// SDK meanwhile fails the test. + async fn replies(&mut self, request_ids: &[u64]) -> HashMap { + let mut replies = HashMap::new(); + timeout(DEADLINE, async { + while request_ids.iter().any(|id| !replies.contains_key(id)) { + let message = read_frame(&mut self.read).await; + assert!( + message.get("method").is_none(), + "an unexpected request: {message}" + ); + let id = message["id"] + .as_u64() + .expect("a reply carries its request's id"); + replies.insert(id, message); + } + }) + .await + .expect("every request is answered"); + replies + } + + async fn stop(self) { + self.client.stop().await.expect("the client stops"); + } +} + +/// A chat-request-shaped JSON body of exactly `len` bytes, dense with quotes and backslashes so +/// its frame carries it escaped, as the runtime's does. +fn escaped_body(len: usize) -> String { + let unit = r#"{"role":"user","content":"a \"quoted\" path C:\\dir\\file, tab\there"},"#; + let mut out = String::with_capacity(len); + out.push_str(r#"{"messages":["#); + while out.len() + unit.len() <= len { + out.push_str(unit); + } + while out.len() < len { + out.push('x'); + } + out +} + +/// Every JSON escape class (quote, backslash, the short escapes, the other control bytes) and +/// multi-byte UTF-8, after a body large enough to span many socket reads. +fn every_escape_class_body() -> String { + let mut out = escaped_body(1024 * 1024); + out.push_str( + "controls: \u{0}\u{1}\u{8}\u{b}\u{c}\u{1f} cr\r lf\n unicode: \u{e9}\u{4e2d}\u{1f600}", + ); + out +} + +#[tokio::test(flavor = "multi_thread")] +#[parallel] +async fn an_escaped_json_body_reaches_the_handler_byte_for_byte() { + let mut peer = Peer::start().await; + let body = every_escape_class_body(); + + peer.start_request("escaped", 10).await; + peer.chunk( + 11, + json!({"requestId": "escaped", "data": body, "end": true}), + ) + .await; + + let captured = peer.captured().await; + assert_eq!(captured.body, body.as_bytes()); + let wire = peer.finish_response(&[10, 11]).await; + assert_wire(&wire, "escaped", &[10, 11]); + peer.stop().await; +} + +#[tokio::test(flavor = "multi_thread")] +#[parallel] +async fn an_empty_body_reaches_the_handler_empty() { + let mut peer = Peer::start().await; + + peer.start_request("empty", 20).await; + peer.chunk(21, json!({"requestId": "empty", "data": "", "end": true})) + .await; + + let captured = peer.captured().await; + assert_eq!(captured.body, Vec::::new()); + let wire = peer.finish_response(&[20, 21]).await; + assert_wire(&wire, "empty", &[20, 21]); + peer.stop().await; +} + +#[tokio::test(flavor = "multi_thread")] +#[parallel] +async fn a_body_in_several_chunks_reaches_the_handler_in_order() { + let mut peer = Peer::start().await; + let parts = [ + escaped_body(64 * 1024), + escaped_body(1000), + escaped_body(70_001), + ]; + + peer.start_request("several", 30).await; + peer.chunk(31, json!({"requestId": "several", "data": parts[0]})) + .await; + // An empty, non-terminal chunk carries no bytes and must not disturb the order. + peer.chunk(32, json!({"requestId": "several", "data": ""})) + .await; + peer.chunk(33, json!({"requestId": "several", "data": parts[1]})) + .await; + peer.chunk( + 34, + json!({"requestId": "several", "data": parts[2], "end": true}), + ) + .await; + + let captured = peer.captured().await; + assert_eq!(captured.body, parts.concat().as_bytes()); + let wire = peer.finish_response(&[30, 31, 32, 33, 34]).await; + assert_wire(&wire, "several", &[30, 31, 32, 33, 34]); + peer.stop().await; +} + +#[tokio::test(flavor = "multi_thread")] +#[parallel] +async fn a_base64_chunk_reaches_the_handler_decoded_before_a_text_chunk() { + let mut peer = Peer::start().await; + + peer.start_request("binary", 40).await; + // "AAEC/w==" is the standard base64 of the bytes 0x00 0x01 0x02 0xff. + peer.chunk( + 41, + json!({"requestId": "binary", "data": "AAEC/w==", "binary": true}), + ) + .await; + peer.chunk( + 42, + json!({"requestId": "binary", "data": "tail", "end": true}), + ) + .await; + + let captured = peer.captured().await; + assert_eq!(captured.body, b"\x00\x01\x02\xfftail".to_vec()); + let wire = peer.finish_response(&[40, 41, 42]).await; + assert_wire(&wire, "binary", &[40, 41, 42]); + peer.stop().await; +} + +#[tokio::test(flavor = "multi_thread")] +#[parallel] +async fn a_chunk_ahead_of_its_start_frame_reaches_the_handler() { + let mut peer = Peer::start().await; + let body = escaped_body(300_001); + + peer.chunk(51, json!({"requestId": "early", "data": body, "end": true})) + .await; + peer.start_request("early", 50).await; + + let captured = peer.captured().await; + assert_eq!(captured.body, body.as_bytes()); + let wire = peer.finish_response(&[50, 51]).await; + assert_wire(&wire, "early", &[50, 51]); + peer.stop().await; +} + +/// A cancel chunk after part of the body, on the `apply_chunk` path that moves `data`: the handler +/// sees the bytes that arrived before it, each request is acked, and the response is its head and +/// then a terminal chunk carrying the cancellation. +#[tokio::test(flavor = "multi_thread")] +#[parallel] +async fn a_cancel_chunk_ends_the_body_and_the_response_with_the_cancellation() { + let mut peer = Peer::start().await; + let part = escaped_body(10_001); + + peer.start_request("cancelled", 80).await; + peer.chunk(81, json!({"requestId": "cancelled", "data": part})) + .await; + peer.chunk( + 82, + json!({"requestId": "cancelled", "data": "", "cancel": true}), + ) + .await; + + let captured = peer.captured().await; + assert_eq!(captured.body, part.as_bytes()); + let wire = peer.finish_response(&[80, 81, 82]).await; + for id in [80, 81, 82] { + assert_eq!( + wire.replies[&id], + json!({"jsonrpc": "2.0", "id": id, "result": {}}), + "the ack of request {id}" + ); + } + assert_eq!(wire.replies.len(), 3, "replies: {:?}", wire.replies); + assert_eq!( + wire.response_frames, + vec![ + ( + "llmInference.httpResponseStart".to_owned(), + json!({"requestId": "cancelled", "status": 200, "headers": {}}), + ), + ( + "llmInference.httpResponseChunk".to_owned(), + json!({ + "requestId": "cancelled", + "data": "", + "end": true, + "error": {"code": "cancelled", "message": "Request cancelled by runtime"} + }), + ), + ] + ); + peer.stop().await; +} + +/// A start or chunk frame whose params do not parse gets the dispatcher's error reply: the params +/// move into the parse instead of being cloned into it, and a failed parse still answers. +#[tokio::test(flavor = "multi_thread")] +#[parallel] +async fn unparsable_start_and_chunk_params_get_the_error_reply() { + let mut peer = Peer::start().await; + + // Neither frame names the requestId that both params types require. + peer.send(&json!({ + "jsonrpc": "2.0", + "id": 70, + "method": "llmInference.httpRequestStart", + "params": {"method": "POST", "url": "http://unused.test/chat", "headers": {}} + })) + .await; + peer.chunk(71, json!({"data": "x", "end": true})).await; + + let replies = peer.replies(&[70, 71]).await; + assert_eq!( + replies[&70], + json!({ + "jsonrpc": "2.0", + "id": 70, + "error": {"code": -32603, "message": "invalid llmInference.httpRequestStart params"} + }) + ); + assert_eq!( + replies[&71], + json!({ + "jsonrpc": "2.0", + "id": 71, + "error": {"code": -32603, "message": "invalid llmInference.httpRequestChunk params"} + }) + ); + peer.stop().await; +} + +/// The mechanism: between the chunk frame's write and the handler's entry, the body is +/// allocated at its exact length once, by the frame parse (`serde_json`'s `Value` string), and +/// the dispatcher moves that buffer to the handler. Copying it instead allocated it three more +/// times (the params `Value` clone, the `data` clone, the drain into an empty `Vec`); reverting +/// any one of the three moves adds one. +#[tokio::test(flavor = "multi_thread")] +#[serial] +async fn a_single_chunk_body_reaches_the_handler_without_a_copy() { + let mut peer = Peer::start().await; + let body = escaped_body(WATCHED_BODY_BYTES); + assert_eq!(body.len(), WATCHED_BODY_BYTES); + peer.start_request("single", 60).await; + // Encoded before the count starts: building the frame allocates the body's length too. + let chunk = frame_bytes(&json!({ + "jsonrpc": "2.0", + "id": 61, + "method": "llmInference.httpRequestChunk", + "params": {"requestId": "single", "data": body, "end": true} + })); + + HITS.store(0, Ordering::SeqCst); + WATCHED.store(WATCHED_BODY_BYTES, Ordering::SeqCst); + timeout(DEADLINE, peer.write.write_all(&chunk)) + .await + .expect("the SDK takes the chunk frame in time") + .expect("the chunk frame to the SDK"); + let captured = peer.captured().await; + WATCHED.store(0, Ordering::SeqCst); + + assert_eq!(captured.body, body.as_bytes()); + assert_eq!( + captured.hits_at_entry, 1, + "allocations of exactly the body's {WATCHED_BODY_BYTES} bytes between the frame's write \ + and the handler's entry: the frame parse's one, and no relay copy" + ); + let wire = peer.finish_response(&[60, 61]).await; + assert_wire(&wire, "single", &[60, 61]); + peer.stop().await; +} diff --git a/samples/startup-policy/README.md b/samples/startup-policy/README.md new file mode 100644 index 0000000000..4e9f902da3 --- /dev/null +++ b/samples/startup-policy/README.md @@ -0,0 +1,28 @@ +# Startup policy example + +This source example uses an isolated configuration directory and a local runtime. +It makes no model request and installs no certificate. +It does not read or write your settings or keychain. + +Prepare the same-checkout runtime and generated Node.js client with the +[SDK contributor guide](../../CONTRIBUTING.md#testing-an-unreleased-runtime-api). +From the SDK directory, run: + +```sh +npx --no-install tsx samples/startup-policy/read-settings.mts /absolute/path/to/runtime.node +``` + +The output shows `user-model`, then `repo-model`, and a `changed` repository edit. +The obsolete value in `config.json` does not override `settings.json`. +Managed plugin controls override the user value. +The startup override disables native web search. +The permission query reports the managed bypass restriction. +The ingestion request disables policy discovery and network access. +The managed sandbox floor stays enabled. +`proxyCaRelevant` is `true`; installation support depends on the host platform. + +The example imports internal host helpers directly from the source checkout. +Those helpers are not public SDK exports. +Applications can use `client.rpc.sandbox.proxyCa.checkRelevance` and +`client.rpc.sandbox.proxyCa.checkInstallationSupport` through the public client. +Neither query installs a certificate or requests elevation. diff --git a/samples/startup-policy/read-settings.mts b/samples/startup-policy/read-settings.mts new file mode 100644 index 0000000000..f0a2c9a883 --- /dev/null +++ b/samples/startup-policy/read-settings.mts @@ -0,0 +1,141 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import assert from "node:assert/strict"; +import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join, resolve } from "node:path"; +import { + createMessageConnection, + StreamMessageReader, + StreamMessageWriter, +} from "vscode-jsonrpc/node.js"; +import { FfiRuntimeHost } from "../../nodejs/src/ffiRuntimeHost.js"; +import { createInternalServerRpc, createServerRpc } from "../../nodejs/src/generated/rpc.js"; + +const library = process.argv[2]; +assert(library, "Pass the same-checkout runtime.node path as the first argument"); +const home = await mkdtemp(join(tmpdir(), "copilot-startup-policy-")); +let host: FfiRuntimeHost | undefined; +let connection: ReturnType | undefined; +const errors: unknown[] = []; +try { + const configDir = join(home, ".copilot"); + const repo = join(home, "project"); + await mkdir(configDir); + await mkdir(join(repo, ".github", "copilot"), { recursive: true }); + await writeFile(join(configDir, "settings.json"), JSON.stringify({ model: "user-model" })); + await writeFile(join(configDir, "config.json"), JSON.stringify({ model: "obsolete-model" })); + await writeFile( + join(repo, ".github", "copilot", "settings.json"), + JSON.stringify({ model: "repo-model" }) + ); + host = FfiRuntimeHost.create( + resolve(library), + undefined, + { + HOME: home, + USERPROFILE: home, + COPILOT_HOME: configDir, + COPILOT_CACHE_HOME: join(home, "cache"), + COPILOT_OFFLINE: "true", + COPILOT_USE_KEYCHAIN: "false", + }, + ["--no-auto-login"] + ); + await host.start(); + connection = createMessageConnection( + new StreamMessageReader(host.receiveStream), + new StreamMessageWriter(host.sendStream) + ); + connection.listen(); + const internal = createInternalServerRpc(connection); + const rpc = createServerRpc(connection); + const startupGates = await internal.featureFlags.resolveStartupGates({ + flags: ["NATIVE_WEB_SEARCH"], + isStaff: false, + isExperimental: false, + isTeam: false, + streamerMode: false, + refreshWithoutSession: false, + environment: {}, + config: { featureFlags: { NATIVE_WEB_SEARCH: false } }, + }); + assert.equal(startupGates.NATIVE_WEB_SEARCH, false); + const loaded = await internal.user.settings.loadWithWarning(); + assert.equal(loaded.settings.model, "user-model"); + const merged = await internal.user.settings.loadAndMergeRepo({ + repoRoot: repo, + userSettings: loaded.settings, + }); + assert.equal(merged.settings.model, "repo-model"); + const edit = await internal.repo.settings.edit({ + repoRoot: repo, + scope: "local", + key: "deniedUrls", + operation: "add-item", + value: "https://example.com", + }); + assert.equal(edit.status, "changed"); + const controls = await internal.managedSettings.applyControls({ + settings: { enabledPlugins: { required: false } }, + managed: { enabledPlugins: { required: true } }, + }); + assert.deepEqual(controls.settingsJson, { enabledPlugins: { required: true } }); + const permissions = await internal.permissions.evaluateManagedPolicy({ + permissions: { disableBypassPermissionsMode: "disable" }, + }); + assert.equal(permissions.bypassPermissionsDisabled, true); + const ingestion = await internal.managedSettings.ingest({ + selfFetch: false, + userAgent: "startup-policy-example", + environment: {}, + platform: process.platform, + homeDirectory: home, + }); + assert.equal(ingestion.serverFetchFailed, false); + const sandbox = await internal.sandbox.resolveFlooredConfig({ + userSandbox: { enabled: false }, + managedSandbox: { enabled: true, failIfUnavailable: true }, + }); + const relevant = await rpc.sandbox.proxyCa.checkRelevance({ + sandboxConfig: { enabled: true, auth: { gh: true } }, + }); + assert.equal(relevant, true); + console.log( + JSON.stringify( + { + userModel: loaded.settings.model, + repoModel: merged.settings.model, + repoEdit: edit.status, + startupGates, + managedControls: controls.settingsJson, + permissions, + ingestion: ingestion.serverResolution, + sandbox: sandbox.sandboxJson, + proxyCaRelevant: relevant, + proxyCaInstallationSupported: await rpc.sandbox.proxyCa.checkInstallationSupport(), + }, + null, + 2 + ) + ); +} catch (error) { + errors.push(error); +} finally { + connection?.dispose(); + try { + await host?.dispose(); + } catch (error) { + errors.push(error); + } + try { + await rm(home, { recursive: true, force: true }); + } catch (error) { + errors.push(error); + } +} +if (errors.length > 0) { + throw new AggregateError(errors, "Startup policy example failed"); +} diff --git a/scripts/ci/check-generated.test.mjs b/scripts/ci/check-generated.test.mjs index 23c95ced04..6827464fdb 100644 --- a/scripts/ci/check-generated.test.mjs +++ b/scripts/ci/check-generated.test.mjs @@ -8,7 +8,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import test from "node:test"; -import { fileURLToPath } from "node:url"; +import { fileURLToPath, pathToFileURL } from "node:url"; import { SCHEMA_FILES } from "../build-prerequisites.mjs"; import { assertGeneratedClean, @@ -64,6 +64,15 @@ function fixture(t) { return { root, git, write, commit, schemas, options }; } +function shallowClone(t, source, depth) { + const root = fs.mkdtempSync(path.join(os.tmpdir(), "sdk-freshness-shallow-")); + t.after(() => fs.rmSync(root, { recursive: true, force: true })); + execFileSync("git", ["clone", "--quiet", "--depth", String(depth), pathToFileURL(source).href, root]); + const git = (...args) => execFileSync("git", args, { cwd: root, encoding: "utf8" }).trim(); + assert.equal(git("rev-parse", "--is-shallow-repository"), "true"); + return { root, git }; +} + for (const name of SCHEMA_FILES) { test(`rejects stale ${name} before SDK generation`, (t) => { const f = fixture(t); @@ -207,10 +216,39 @@ test("uses the tested PR merge's actual base, not an earlier event base", (t) => const actualBase = f.commit(); f.git("merge", "--quiet", "--no-ff", "topic", "-m", "test merge"); assert.equal(generationBaseline(f.root, "pull_request", { pull_request: { base: { sha: oldBase } } }), actualBase); + const clone = shallowClone(t, f.root, 2); + assert.equal( + generationBaseline(clone.root, "pull_request", { pull_request: { base: { sha: oldBase } } }), + actualBase, + ); const paths = f.git("diff", "--name-only", actualBase, "HEAD").split("\n"); assert.equal(requiresSdkGeneration(paths), false); }); +for (const eventName of ["merge_group", "push"]) { + test(`uses a separately fetched ${eventName} baseline beyond shallow history`, (t) => { + const f = fixture(t); + f.write("generated/api.schema.json", '{"earlierCommit":true}\n'); + f.commit(); + f.write("internal", "last commit"); + f.commit(); + const clone = shallowClone(t, f.root, 1); + const event = + eventName === "merge_group" + ? { merge_group: { base_sha: f.options.baseline } } + : { before: f.options.baseline }; + assert.throws(() => generationBaseline(clone.root, eventName, event)); + clone.git("fetch", "--quiet", "--no-tags", "--depth=1", "origin", f.options.baseline); + const baseline = generationBaseline(clone.root, eventName, event); + assert.equal(baseline, f.options.baseline); + assert.equal( + requiresSdkGeneration(clone.git("diff", "--name-only", baseline, "HEAD").split("\n")), + true, + ); + assert.equal(clone.git("rev-parse", "--is-shallow-repository"), "true"); + }); +} + test("uses the whole merge-group/push range, not just the final commit", (t) => { const f = fixture(t); f.write("generated/api.schema.json", '{"earlierCommit":true}\n'); diff --git a/scripts/ci/run-dotnet-tests.sh b/scripts/ci/run-dotnet-tests.sh index ea0312c4a6..b1840e323e 100755 --- a/scripts/ci/run-dotnet-tests.sh +++ b/scripts/ci/run-dotnet-tests.sh @@ -11,6 +11,7 @@ Usage: run-dotnet-tests.sh [--help] Runs the full .NET SDK test project. Environment variables: DOTNET_TEST_FILTER Optional dotnet test filter (e.g. for a backend or transport). DOTNET_TEST_RUNTIME Optional runtime identifier passed to dotnet test. + DOTNET_TEST_FRAMEWORK Optional target framework (default: all project frameworks). DOTNET_TEST_RESULTS_DIRECTORY Results directory (default: TestResults). EOF } @@ -40,6 +41,7 @@ args=( ) filter="${DOTNET_TEST_FILTER:-}" runtime="${DOTNET_TEST_RUNTIME:-}" +framework="${DOTNET_TEST_FRAMEWORK:-}" if [[ -n "$filter" ]]; then args+=(--filter "$filter") @@ -47,5 +49,8 @@ fi if [[ -n "$runtime" ]]; then args+=(--runtime "$runtime") fi +if [[ -n "$framework" ]]; then + args+=(--framework "$framework") +fi dotnet test "${args[@]}" diff --git a/scripts/ci/run-dotnet-tests.test.mjs b/scripts/ci/run-dotnet-tests.test.mjs index 582fcd6b2c..f979b8042c 100644 --- a/scripts/ci/run-dotnet-tests.test.mjs +++ b/scripts/ci/run-dotnet-tests.test.mjs @@ -19,7 +19,7 @@ function fromBashPath(value) { return value; } -await test("runs all .NET tests with optional filter, runtime, and results directory", (t) => { +await test("runs all .NET tests with optional filter, runtime, framework, and results directory", (t) => { const root = fs.mkdtempSync(path.join(os.tmpdir(), "sdk-dotnet-test-")); t.after(() => fs.rmSync(root, { recursive: true, force: true })); const argumentsPath = path.join(root, "arguments"); @@ -31,9 +31,12 @@ await test("runs all .NET tests with optional filter, runtime, and results direc ); fs.chmodSync(fakeDotnet, 0o755); - for (const [filter, runtime, resultsDirectory] of [ - ["", "", ""], - ["E2EBackend!=CapiOnly", "win-x64", "TestResults/inprocess"], + for (const [filter, runtime, framework, resultsDirectory] of [ + ["", "", "", ""], + ["E2EBackend!=CapiOnly", "win-x64", "net8.0", "TestResults/inprocess"], + ["", "win-x64", "net472", "TestResults/subprocess"], + ["FullyQualifiedName~GitHub.Copilot.Test.E2E.S", "win-x64", "net8.0", "TestResults/subprocess"], + ["FullyQualifiedName!~GitHub.Copilot.Test.E2E.S", "win-x64", "net472", "TestResults/subprocess"], ]) { const result = spawnSync("bash", [script], { encoding: "utf8", @@ -42,6 +45,7 @@ await test("runs all .NET tests with optional filter, runtime, and results direc ARGUMENTS_PATH: argumentsPath, DOTNET_TEST_FILTER: filter, DOTNET_TEST_RUNTIME: runtime, + DOTNET_TEST_FRAMEWORK: framework, DOTNET_TEST_RESULTS_DIRECTORY: resultsDirectory, PATH: `${root}${path.delimiter}${process.env.PATH ?? ""}`, WORKING_DIRECTORY_PATH: workingDirectoryPath, @@ -67,6 +71,10 @@ await test("runs all .NET tests with optional filter, runtime, and results direc if (runtime) { assert.equal(arguments_[arguments_.indexOf("--runtime") + 1], runtime); } + assert.equal(arguments_.includes("--framework"), framework !== ""); + if (framework) { + assert.equal(arguments_[arguments_.indexOf("--framework") + 1], framework); + } } }); @@ -75,6 +83,7 @@ await test("documents its environment and rejects positional arguments", () => { assert.equal(help.status, 0, help.stderr); assert.match(help.stdout, /DOTNET_TEST_FILTER/); assert.match(help.stdout, /DOTNET_TEST_RUNTIME/); + assert.match(help.stdout, /DOTNET_TEST_FRAMEWORK/); assert.match(help.stdout, /DOTNET_TEST_RESULTS_DIRECTORY/); const invalid = spawnSync("bash", [script, "unexpected"], { encoding: "utf8" }); diff --git a/scripts/codegen/csharp-dictionary-roundtrip.test.ts b/scripts/codegen/csharp-dictionary-roundtrip.test.ts new file mode 100644 index 0000000000..604ed86fc2 --- /dev/null +++ b/scripts/codegen/csharp-dictionary-roundtrip.test.ts @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import assert from "node:assert/strict"; +import { spawnSync } from "node:child_process"; +import { mkdtemp, rm, writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import { test } from "node:test"; +import { generateRpcCode } from "./csharp.ts"; + +test("generated C# open dictionaries retain entries through System.Text.Json", async () => { + const directory = await mkdtemp(join(process.cwd(), ".csharp-dictionary-test-")); + let failure: unknown; + try { + const code = generateRpcCode({ + server: { + sample: { + read: { + rpcMethod: "sample.read", + params: null, + result: { + title: "SampleResult", + type: "object", + properties: { + environment: { type: "object", properties: {} }, + settings: { type: "object", properties: {} }, + }, + required: ["environment"], + }, + }, + }, + }, + }); + await writeFile(join(directory, "Rpc.cs"), code); + await writeFile( + join(directory, "Roundtrip.csproj"), + ` + + Exe + net10.0 + enable + enable + +` + ); + await writeFile( + join(directory, "Program.cs"), + `using System.Text.Json; +using System.Text.Json.Nodes; +using GitHub.Copilot.Rpc; + +const string json = """ +{"environment":{"COPILOT_HOME":"/isolated/config","EMPTY":""},"settings":{"model":"selected-model","enabled":false,"custom":{"nested":42}}} +"""; +var value = JsonSerializer.Deserialize(json)!; +var actual = JsonSerializer.Serialize(value); +if (!JsonNode.DeepEquals(JsonNode.Parse(json), JsonNode.Parse(actual))) + throw new Exception($"Dictionary entries lost: {actual}"); +Console.WriteLine("environment and settings dictionaries round-trip"); + +namespace GitHub.Copilot.Rpc +{ + internal sealed class JsonRpc; + internal static class CopilotClient + { + internal static Task InvokeRpcAsync(JsonRpc rpc, string method, object[] args, + CancellationToken cancellationToken) => throw new NotSupportedException("No RPC is expected"); + } +}` + ); + const result = spawnSync("dotnet", ["run", "--project", join(directory, "Roundtrip.csproj")], { + cwd: directory, + encoding: "utf8", + }); + if (result.error) throw result.error; + assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`); + assert.match(result.stdout, /environment and settings dictionaries round-trip/); + } catch (error) { + failure = error; + throw error; + } finally { + try { + await rm(directory, { recursive: true, force: true }); + } catch (cleanupError) { + if (failure) throw new AggregateError([failure, cleanupError], "Test and cleanup failed"); + throw cleanupError; + } + } +}); diff --git a/scripts/codegen/csharp.ts b/scripts/codegen/csharp.ts index faef5dd771..fe7e8f185a 100644 --- a/scripts/codegen/csharp.ts +++ b/scripts/codegen/csharp.ts @@ -1894,9 +1894,12 @@ function resolveRpcType(schema: JSONSchema7, isRequired: boolean, parentClassNam if (isOpaqueJson(schema)) { return isRequired ? "JsonElement" : "JsonElement?"; } - if (schema.type === "object" && schema.additionalProperties === true && + if (schema.type === "object" && schema.additionalProperties !== false && (!schema.properties || Object.keys(schema.properties).length === 0)) { - return isRequired ? "IDictionary" : "IDictionary?"; + const valueType = typeof schema.additionalProperties === "object" + ? resolveRpcType(schema.additionalProperties, true, parentClassName, `${propName}Value`, classes) + : "JsonElement"; + return isRequired ? `IDictionary` : `IDictionary?`; } // Handle $ref by resolving against schema definitions and generating the referenced class if (schema.$ref) { @@ -1911,7 +1914,8 @@ function resolveRpcType(schema: JSONSchema7, isRequired: boolean, parentClassNam return isRequired ? enumName : `${enumName}?`; } - if (refSchema.type === "object" && refSchema.properties) { + if (refSchema.type === "object" && refSchema.properties && + (Object.keys(refSchema.properties).length > 0 || refSchema.additionalProperties === false)) { const cls = emitRpcClass(typeName, refSchema, "public", classes); if (cls) classes.push(cls); return isRequired ? typeName : `${typeName}?`; @@ -2005,7 +2009,8 @@ function resolveRpcType(schema: JSONSchema7, isRequired: boolean, parentClassNam } if (schema.type === "array" && schema.items) { const items = schema.items as JSONSchema7; - if (items.type === "object" && items.properties) { + if (items.type === "object" && items.properties && + (Object.keys(items.properties).length > 0 || items.additionalProperties === false)) { const itemClass = (items.title as string) ?? `${parentClassName}${singularPascal(propName)}`; classes.push(emitRpcClass(itemClass, items, "public", classes)); return isRequired ? `IList<${itemClass}>` : `IList<${itemClass}>?`; @@ -2013,11 +2018,6 @@ function resolveRpcType(schema: JSONSchema7, isRequired: boolean, parentClassNam const itemType = resolveRpcType(items, true, parentClassName, `${propName}Item`, classes); return isRequired ? `IList<${itemType}>` : `IList<${itemType}>?`; } - if (schema.type === "object" && schema.additionalProperties && typeof schema.additionalProperties === "object") { - const vs = schema.additionalProperties as JSONSchema7; - const valueType = resolveRpcType(vs, true, parentClassName, `${propName}Value`, classes); - return isRequired ? `IDictionary` : `IDictionary?`; - } return schemaTypeToCSharp(schema, isRequired, rpcKnownTypes, propName); } @@ -2312,7 +2312,7 @@ function emitServerInstanceMethod( const opaqueListOptional = naturalType === "IList?"; const opaque = opaqueRequired || opaqueOptional || opaqueListRequired || opaqueListOptional; const csType = opaqueRequired - ? "object" + ? "object?" : opaqueOptional ? "object?" : opaqueListRequired @@ -2322,7 +2322,7 @@ function emitServerInstanceMethod( : naturalType; sigParams.push(`${csType} ${pName}${isReq ? "" : " = null"}`); const assignedValue = opaqueRequired - ? `CopilotClient.ToJsonElementForWire(${pName})!.Value` + ? `CopilotClient.ToJsonElementForWire(${pName}) ?? JsonElement.Parse("null")` : opaqueOptional ? `CopilotClient.ToJsonElementForWire(${pName})` : opaqueListRequired @@ -2331,7 +2331,7 @@ function emitServerInstanceMethod( ? `${pName}?.Select(static v => CopilotClient.ToJsonElementForWire(v)!.Value).ToList()` : pName; bodyAssignments.push(`${csharpName} = ${assignedValue}`); - if (opaqueRequired || opaqueListRequired || (!opaque && requiresArgumentNullCheck(csType, isReq))) { + if (opaqueListRequired || (!opaque && requiresArgumentNullCheck(csType, isReq))) { argumentNullChecks.push(`${indent} ArgumentNullException.ThrowIfNull(${pName});`); } if (opaqueListRequired || (!opaque && requiresArgumentNullCheck(csType, isReq))) { @@ -2364,7 +2364,18 @@ function emitServerInstanceMethod( } if (requestClassName && bodyAssignments.length > 0) { lines.push(`${indent} var ${localRequestName} = new ${requestClassName} { ${bodyAssignments.join(", ")} };`); - if (!isVoidSchema(resultSchema)) { + if ( + method.rpcMethod === "user.settings.applyTopKey" || + method.rpcMethod === "repo.settings.writeKey" + ) { + lines.push(`${indent} var payload = JsonSerializer.SerializeToNode(${localRequestName}, RpcJsonContext.Default.${requestClassName})!.AsObject();`); + lines.push(`${indent} if (${localRequestName}.Delete != true && ${localRequestName}.Value is null) payload["value"] = null;`); + if (!isVoidSchema(resultSchema)) { + lines.push(`${indent} return await CopilotClient.InvokeRpcAsync<${resultClassName}>(_rpc, "${method.rpcMethod}", [payload], cancellationToken);`); + } else { + lines.push(`${indent} await CopilotClient.InvokeRpcAsync(_rpc, "${method.rpcMethod}", [payload], cancellationToken);`); + } + } else if (!isVoidSchema(resultSchema)) { lines.push(`${indent} return await CopilotClient.InvokeRpcAsync<${resultClassName}>(_rpc, "${method.rpcMethod}", [${localRequestName}], cancellationToken);`); } else { lines.push(`${indent} await CopilotClient.InvokeRpcAsync(_rpc, "${method.rpcMethod}", [${localRequestName}], cancellationToken);`); @@ -2548,7 +2559,7 @@ function emitSessionMethod(key: string, method: RpcMethod, lines: string[], clas const opaqueListOptional = naturalType === "IList?"; const opaque = opaqueRequired || opaqueOptional || opaqueListRequired || opaqueListOptional; const csType = opaqueRequired - ? "object" + ? "object?" : opaqueOptional ? "object?" : opaqueListRequired @@ -2558,7 +2569,7 @@ function emitSessionMethod(key: string, method: RpcMethod, lines: string[], clas : naturalType; sigParams.push(`${csType} ${pName}${isReq ? "" : " = null"}`); const assignedValue = opaqueRequired - ? `CopilotClient.ToJsonElementForWire(${pName})!.Value` + ? `CopilotClient.ToJsonElementForWire(${pName}) ?? JsonElement.Parse("null")` : opaqueOptional ? `CopilotClient.ToJsonElementForWire(${pName})` : opaqueListRequired @@ -2567,7 +2578,7 @@ function emitSessionMethod(key: string, method: RpcMethod, lines: string[], clas ? `${pName}?.Select(static v => CopilotClient.ToJsonElementForWire(v)!.Value).ToList()` : pName; bodyAssignments.push(`${csharpName} = ${assignedValue}`); - if (opaqueRequired || opaqueListRequired || (!opaque && requiresArgumentNullCheck(csType, isReq))) { + if (opaqueListRequired || (!opaque && requiresArgumentNullCheck(csType, isReq))) { argumentNullChecks.push(`${indent} ArgumentNullException.ThrowIfNull(${pName});`); } if (opaqueListRequired || (!opaque && requiresArgumentNullCheck(csType, isReq))) { @@ -2599,7 +2610,18 @@ function emitSessionMethod(key: string, method: RpcMethod, lines: string[], clas lines.push(`${indent} _session.ThrowIfDisposed();`); lines.push(""); lines.push(`${indent} var ${localRequestName} = new ${wireRequestClassName} { ${bodyAssignments.join(", ")} };`); - if (!isVoidSchema(resultSchema)) { + if ( + method.rpcMethod === "user.settings.applyTopKey" || + method.rpcMethod === "repo.settings.writeKey" + ) { + lines.push(`${indent} var payload = JsonSerializer.SerializeToNode(${localRequestName}, RpcJsonContext.Default.${wireRequestClassName})!.AsObject();`); + lines.push(`${indent} if (${localRequestName}.Delete != true && ${localRequestName}.Value is null) payload["value"] = null;`); + if (!isVoidSchema(resultSchema)) { + lines.push(`${indent} return await CopilotClient.InvokeRpcAsync<${resultClassName}>(_session.Rpc, "${method.rpcMethod}", [payload], cancellationToken);`, `${indent}}`); + } else { + lines.push(`${indent} await CopilotClient.InvokeRpcAsync(_session.Rpc, "${method.rpcMethod}", [payload], cancellationToken);`, `${indent}}`); + } + } else if (!isVoidSchema(resultSchema)) { lines.push(`${indent} return await CopilotClient.InvokeRpcAsync<${resultClassName}>(_session.Rpc, "${method.rpcMethod}", [${localRequestName}], cancellationToken);`, `${indent}}`); } else { lines.push(`${indent} await CopilotClient.InvokeRpcAsync(_session.Rpc, "${method.rpcMethod}", [${localRequestName}], cancellationToken);`, `${indent}}`); diff --git a/scripts/codegen/go.ts b/scripts/codegen/go.ts index 378d36678a..b2a75aea37 100644 --- a/scripts/codegen/go.ts +++ b/scripts/codegen/go.ts @@ -4090,9 +4090,10 @@ async function generateRpc(schemaPath?: string): Promise { } } for (const typeName of internalTypeNames) { + const emittedTypeName = resolveType(typeName); generatedTypeCode = generatedTypeCode.replace( - new RegExp(`^(type ${typeName} struct)`, "m"), - `// Internal: ${typeName} is an internal SDK API and is not part of the public surface.\n$1` + new RegExp(`^(type ${escapeRegExp(emittedTypeName)} struct)`, "m"), + `// Internal: ${emittedTypeName} is an internal SDK API and is not part of the public surface.\n$1` ); } // Remove trailing blank lines before appending. @@ -4303,6 +4304,7 @@ function emitMethod(lines: string[], receiver: string, name: string, method: Rpc const paramsType = hasParams ? resolveType(goParamsTypeName(method)) : ""; const hasRequiredNonSessionParams = nonSessionParams.some((name) => requiredParams.has(name)); const paramsAreOptional = hasParams && !!method.params && !!getNullableInner(method.params) && !hasRequiredNonSessionParams; + const supportsExplicitNullValue = method.rpcMethod === "user.settings.edit"; // For wrapper-level methods, access fields through a.common; for service type aliases, use a directly const clientRef = isWrapper ? "a.common.client" : "a.client"; @@ -4361,14 +4363,41 @@ function emitMethod(lines: string[], receiver: string, name: string, method: Rpc } lines.push(`\traw, err := ${clientRef}.Request(ctx, "${method.rpcMethod}", req)`); } else { - if (method.rpcMethod === "managedSettings.resolve") { - // A typed nil inside Request's any argument marshals as null, not omitted params. - lines.push(`\tif ${paramsRef} == nil {`); - lines.push(`\t\t${paramsRef} = &${paramsType}{}`); + if (hasParams && supportsExplicitNullValue) { + lines.push(`\treq := map[string]any{}`); + lines.push(`\tif ${paramsRef} != nil {`); + for (const pName of nonSessionParams) { + const field = fields.get(paramsType)?.get(pName); + const goField = field?.name ?? toGoFieldName(pName); + const goType = field?.type; + const isOptional = !requiredParams.has(pName); + if (isOptional) { + lines.push(`\t\tif ${paramsRef}.${goField} != nil {`); + const valueExpr = goOptionalFieldNeedsDereference(goType) ? `*${paramsRef}.${goField}` : `${paramsRef}.${goField}`; + lines.push(`\t\t\treq["${pName}"] = ${valueExpr}`); + lines.push(`\t\t}`); + } else { + lines.push(`\t\treq["${pName}"] = ${paramsRef}.${goField}`); + } + } + const operationField = fields.get(paramsType)?.get("operation")?.name ?? toGoFieldName("operation"); + lines.push(`\t\tif ${paramsRef}.${operationField} != SettingsEditOperationRemove {`); + lines.push(`\t\t\tif _, ok := req["value"]; !ok {`); + lines.push(`\t\t\t\treq["value"] = nil`); + lines.push(`\t\t\t}`); + lines.push(`\t\t}`); lines.push(`\t}`); + lines.push(`\traw, err := ${clientRef}.Request(ctx, "${method.rpcMethod}", req)`); + } else { + if (method.rpcMethod === "managedSettings.resolve") { + // A typed nil inside Request's any argument marshals as null, not omitted params. + lines.push(`\tif ${paramsRef} == nil {`); + lines.push(`\t\t${paramsRef} = &${paramsType}{}`); + lines.push(`\t}`); + } + const arg = hasParams ? paramsRef : "nil"; + lines.push(`\traw, err := ${clientRef}.Request(ctx, "${method.rpcMethod}", ${arg})`); } - const arg = hasParams ? paramsRef : "nil"; - lines.push(`\traw, err := ${clientRef}.Request(ctx, "${method.rpcMethod}", ${arg})`); } lines.push(`\tif err != nil {`); diff --git a/scripts/codegen/python.ts b/scripts/codegen/python.ts index 237ebf216f..ceb5598d80 100644 --- a/scripts/codegen/python.ts +++ b/scripts/codegen/python.ts @@ -1854,6 +1854,58 @@ function pythonParamsTypeName(method: RpcMethod): string { return getRpcSchemaTypeName(schema, fallback); } +function applyExplicitNullableFieldPresenceSentinel( + code: string, + resolveType: (name: string) => string +): string { + const escapeRegex = (value: string): string => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const explicitNullableFields: Array<{ className: string; jsonName: string; pyName: string }> = [ + { + className: resolveType("UserSettingsApplyTopKeyRequest"), + jsonName: "value", + pyName: "value", + }, + { + className: resolveType("RepoSettingsWriteKeyRequest"), + jsonName: "value", + pyName: "value", + }, + { + className: resolveType("UserSettingsEditRequest"), + jsonName: "value", + pyName: "value", + }, + ]; + + let result = code; + for (const { className, jsonName, pyName } of explicitNullableFields) { + const classPattern = new RegExp( + `(@dataclass\\r?\\nclass ${escapeRegex(className)}\\b[\\s\\S]*?\\r?\\n)(?=@dataclass\\r?\\nclass |class\\s+\\w+\\(Enum\\):|def\\s+\\w+\\(|$)`, + "g" + ); + result = result.replace(classPattern, (classBlock) => { + let next = classBlock; + next = next.replace( + new RegExp(`^(\\s{4}${escapeRegex(pyName)}:\\s[^=\\r\\n]+\\=\\s*)None(\\r?\\n)`, "m"), + "$1UNSET_SENTINEL$2" + ); + next = next.replace( + new RegExp( + `^(\\s{8}${escapeRegex(pyName)}\\s*=\\s*)(.+?)obj\\.get\\("${escapeRegex(jsonName)}"\\)(.*)$`, + "m" + ), + `$1$2obj["${jsonName}"]$3 if "${jsonName}" in obj else UNSET_SENTINEL` + ); + next = next.replace( + new RegExp(`^(\\s{8}if\\s+self\\.${escapeRegex(pyName)}\\s+is\\s+not\\s+)None(\\s*:\\s*\\r?\\n)`, "m"), + "$1UNSET_SENTINEL$2" + ); + return next; + }); + } + return result; +} + // ── Session Events ────────────────────────────────────────────────────────── // ── Session Events (custom codegen — dedicated per-event payload types) ───── @@ -3436,6 +3488,25 @@ async function generateRpc(schemaPath?: string, sessionEventsSchema?: JSONSchema const allDefinitions = combinedSchema.definitions! as Record; preservePythonRpcStringDateFields(allDefinitions); + const managedOperationProperties = resolveObjectSchema( + allDefinitions.ManagedPermissionOperation, + rpcDefinitions + )?.properties; + const managedOperationKind = managedOperationProperties?.kind; + if (managedOperationProperties && managedOperationKind && typeof managedOperationKind === "object") { + const values = + managedOperationKind.enum ?? + (typeof managedOperationKind.const === "string" ? [managedOperationKind.const] : undefined); + if (!values?.every((value): value is string => typeof value === "string")) { + throw new Error("Expected a string discriminator for ManagedPermissionOperation.kind"); + } + allDefinitions.ManagedPermissionOperationKind = { + type: "string", + enum: values, + description: managedOperationKind.description, + }; + managedOperationProperties.kind = { $ref: "#/definitions/ManagedPermissionOperationKind" }; + } const allDefinitionCollections: DefinitionCollections = { definitions: { ...(combinedSchema.$defs ?? {}), ...allDefinitions }, $defs: { ...allDefinitions, ...(combinedSchema.$defs ?? {}) }, @@ -3455,13 +3526,13 @@ async function generateRpc(schemaPath?: string, sessionEventsSchema?: JSONSchema }; const externalRefs = rewriteExternalRefsForPython(singleSchema as JSONSchema7 & { definitions?: Record }); const externalEnumNames = collectPythonExternalEnumNames(sessionEventsSchema, externalRefs.placeholderNames); - // Discovery and session availability are independently versioned despite identical values. - // Hide the discovery enum from quicktype's enum merging, then restore it with our enum emitter. + // Independently versioned enums must not be structurally merged by quicktype. + // Hide them from quicktype's enum merging, then restore them with our enum emitter. const distinctEnums: Pick = { enumsByName: new Map(), enums: [], }; - for (const name of ["ConnectorDiscoveryAvailability"]) { + for (const name of ["ConnectorDiscoveryAvailability", "ManagedPermissionOperationKind"]) { const definition = allDefinitions[name]; if (!definition) continue; if (!definition.enum?.every((value): value is string => typeof value === "string")) { @@ -3760,6 +3831,7 @@ import dateutil.parser T = TypeVar("T") EnumT = TypeVar("EnumT", bound=Enum) +UNSET_SENTINEL = object() `); lines.push(typesCode); @@ -3854,6 +3926,7 @@ def _patch_model_capabilities(data: dict) -> dict: finalCode, new Set(Object.keys(allDefinitions).map((name) => name.toLowerCase())) ); + finalCode = applyExplicitNullableFieldPresenceSentinel(finalCode, resolveType); // Apply `_`-prefix to type names of internal RPC types so the leading-underscore // Python convention signals "internal, no stability guarantees" to consumers. @@ -3870,7 +3943,11 @@ def _patch_model_capabilities(data: dict) -> dict: } } if (internalDefs.size > 0) { - finalCode = renameInternalPythonSymbols(finalCode, internalDefs); + // Resolve class-name casing without following aliases to shared public types. + const internalSymbols = new Set( + [...internalDefs].flatMap((name) => [name, actualTypeNames.get(name.toLowerCase()) ?? name]) + ); + finalCode = renameInternalPythonSymbols(finalCode, internalSymbols); } } @@ -4232,10 +4309,16 @@ export function emitMethod(lines: string[], name: string, method: RpcMethod, isS if (isSession) { if (hasParams) { + const serializeParams = + method.rpcMethod === "user.settings.applyTopKey" || + method.rpcMethod === "repo.settings.writeKey" || + method.rpcMethod === "user.settings.edit" + ? "params.to_dict()" + : "{k: v for k, v in params.to_dict().items() if v is not None}"; if (paramsOptional) { - lines.push(` 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 {}`); + lines.push(` params_dict: dict[str, Any] = ${serializeParams} if params is not None else {}`); } else { - lines.push(` params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None}`); + lines.push(` params_dict: dict[str, Any] = ${serializeParams}`); } lines.push(` params_dict["sessionId"] = self._session_id`); emitRequestCall("params_dict"); @@ -4244,10 +4327,16 @@ export function emitMethod(lines: string[], name: string, method: RpcMethod, isS } } else { if (hasParams) { + const serializeParams = + method.rpcMethod === "user.settings.applyTopKey" || + method.rpcMethod === "repo.settings.writeKey" || + method.rpcMethod === "user.settings.edit" + ? "params.to_dict()" + : "{k: v for k, v in params.to_dict().items() if v is not None}"; if (paramsOptional) { - lines.push(` params_dict = {k: v for k, v in params.to_dict().items() if v is not None} if params is not None else {}`); + lines.push(` params_dict = ${serializeParams} if params is not None else {}`); } else { - lines.push(` params_dict = {k: v for k, v in params.to_dict().items() if v is not None}`); + lines.push(` params_dict = ${serializeParams}`); } emitRequestCall("params_dict"); } else { diff --git a/scripts/codegen/rust.ts b/scripts/codegen/rust.ts index 9ce9f54511..20b6f100d7 100644 --- a/scripts/codegen/rust.ts +++ b/scripts/codegen/rust.ts @@ -804,8 +804,11 @@ function resolveRustType( ); return wrapOption(typeName, isRequired); } - if (isObjectSchema(resolved)) { - emitRustStruct(typeName, resolved, ctx); + const namedObject = typeName === "ManagedPermissionOperation" + ? resolveObjectSchema(resolved, ctx.definitions) + : resolved; + if (namedObject && isObjectSchema(namedObject)) { + emitRustStruct(typeName, { ...resolved, ...namedObject }, ctx); return wrapOption(typeName, isRequired); } return resolveRustType( @@ -2519,7 +2522,9 @@ function pushNamespaceMethodBody( // Build the params Value sent over the wire. if (isSession) { if (hasParams) { - out.push(` let mut wire_params = serde_json::to_value(params)?;`); + out.push( + ` let mut wire_params = serde_json::to_value(params)?;`, + ); out.push( ` wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string());`, ); @@ -2533,7 +2538,9 @@ function pushNamespaceMethodBody( ); } else { if (hasParams) { - out.push(` let wire_params = serde_json::to_value(params)?;`); + out.push( + ` let wire_params = serde_json::to_value(params)?;`, + ); } else { out.push(` let wire_params = serde_json::json!({});`); } diff --git a/scripts/corrections/package-lock.json b/scripts/corrections/package-lock.json index a975812af3..faa7a956f7 100644 --- a/scripts/corrections/package-lock.json +++ b/scripts/corrections/package-lock.json @@ -10,7 +10,7 @@ "@octokit/rest": "^22.0.1", "@types/node": "^22.0.0", "typescript": "^5.8.0", - "vitest": "^4.1.0" + "vitest": "^4.1.11" } }, "node_modules/@actions/github": { @@ -40,66 +40,13 @@ "undici": "^6.23.0" } }, - "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", "dev": true, "license": "MIT" }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" - } - }, "node_modules/@octokit/auth-token": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", @@ -268,19 +215,36 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.153.0.tgz", + "integrity": "sha512-jSNrRIyvPVLL5E1z/tS5zLAprosGz/FuI1PWwEOz59j3E4TTTzSpqKb/RDGqJhhW0QHYISGXxq582wGx6T11Tw==", "dev": true, "license": "MIT", "funding": { - "url": "https://github.com/sponsors/Boshen" + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.13.tgz", + "integrity": "sha512-Vu/NICrgdRvUOeaJZIyB2FP63s6fO/4ud5l8dXmxDx9yDyn+mN0y+gsvsVviSli+T361Aqo0h46EuMoobrwawA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", - "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.13.tgz", + "integrity": "sha512-6LEoIrGy5vvCd2boNnv2soPSUZj/PRvhp8R/t9KlX+mIZxThIE/ZqxrGXxmuNX0FuvQFvLdZr7S4HrYpYBVhEQ==", "cpu": [ "arm64" ], @@ -295,9 +259,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", - "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.13.tgz", + "integrity": "sha512-CaJSRzlQ6Q+zkDMVA9OkVxBjVoPRCDEE3v0FSJ9kIQBgRjFApA3Si4ijhbNVUji7ZJt+PfO0e46qy4nPInHUPA==", "cpu": [ "arm64" ], @@ -312,9 +276,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", - "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.13.tgz", + "integrity": "sha512-e4r340I5hxSzJkD3YJBxgzq/rJ3wwxwdLLb2FPt1Zpyem20nrQPT11DDEEkazwxaHumH8wpglsp/Ozgj/wT6mw==", "cpu": [ "x64" ], @@ -329,9 +293,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", - "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.13.tgz", + "integrity": "sha512-hoBWDZ4/K/mJKVMbsgUd1jfpA2DiOrJ54mpegObU9TBHBBrtX4FLIXJJQRDPZ/2Dn91GvPob+Z2hwKJzf4E5SQ==", "cpu": [ "x64" ], @@ -346,9 +310,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", - "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.13.tgz", + "integrity": "sha512-oF9fZu3vTWnTiVAkDFxU9PJLt1nh20K9RRpuHsR7ZnE2L7XookBbDBVLojCVSLGbvtSlxeUNnjAj6DbNsKyzbg==", "cpu": [ "arm" ], @@ -363,13 +327,16 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", - "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.13.tgz", + "integrity": "sha512-DTpb/+jMiqWMPgphn4ksW1zW+XLJ6dJJ9FMvnIk9I291LhCctMPhMYJrP5t9p6C9qa3KXadDdUEZHBaPh3kNxQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -380,13 +347,16 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", - "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.13.tgz", + "integrity": "sha512-xU+Kvv9N2A3SY35zFh1FWWNjxbadE/BKeqnkXe7vScU4GZsw757BlVmDLITZEq8DPpk6m7uoDYkK7P+x/uU5Ow==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -397,13 +367,16 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", - "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.13.tgz", + "integrity": "sha512-XMk/hnhh25oQ756Lclb1Q11h4ll0xC0Esa5w4Vzp+mAE2S3AGJgeM0SonrIgY8fHhjarb6hcoTuwoFG0VVP2ig==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -414,13 +387,16 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", - "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.13.tgz", + "integrity": "sha512-U1BoTHYmlywE2n3HUWjzZ+8EQsKr+OwpxYuDWaubx42tUL8QSL7nOqRz/NVHanibbFP3M1Rxuprz/wNcbMKjqw==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -431,13 +407,16 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", - "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.13.tgz", + "integrity": "sha512-J4cm80Pc6wEwV5AZ42o62sIQgDk+Y7R2s646jIwuvO+4V46QB58N4vVuMPWCBaNgursXLpmUWJV//WQAkPvHBQ==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -448,13 +427,16 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", - "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.13.tgz", + "integrity": "sha512-W0euZS40U4V6JkmEnVCIX9mcsL6cbXQ4X8c2w+MMOIjFg68TXUW+u3RT+2e1t1pv9mg/EP3pDtK3POXdQ7Rxyw==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -465,9 +447,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", - "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.13.tgz", + "integrity": "sha512-do/AF/4n9MRshia3a5aXoy8xM2c6mgfT6D7H/dY6my5z+hfCIR4ab7mk4IRj++nsEQpKy39kd0DQnDq+r692tw==", "cpu": [ "arm64" ], @@ -481,29 +463,10 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", - "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", - "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.13.tgz", + "integrity": "sha512-6FtUf2cJZobutJdxnGYX8z0vtbgqU0b2v8vfBhkXb9wp2MppMFtkunE+IaOntyi01rkIQoPW2GHABtfzsC0BRQ==", "cpu": [ "arm64" ], @@ -518,9 +481,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", - "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.13.tgz", + "integrity": "sha512-UKhFaZTeTcbKrb3iU82m17jVcPEAoqDkCN5MBqgPyZSwp/cQmib21crTS0DdH7T1B7wZRWEI89YQAOJMet5yhg==", "cpu": [ "x64" ], @@ -548,17 +511,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", @@ -595,31 +547,31 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.0.tgz", - "integrity": "sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz", + "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.0", - "@vitest/utils": "4.1.0", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", "chai": "^6.2.2", - "tinyrainbow": "^3.0.3" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.0.tgz", - "integrity": "sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz", + "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.0", + "@vitest/spy": "4.1.11", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -628,7 +580,7 @@ }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "msw": { @@ -640,26 +592,26 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.0.tgz", - "integrity": "sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz", + "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^3.0.3" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.0.tgz", - "integrity": "sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz", + "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.0", + "@vitest/utils": "4.1.11", "pathe": "^2.0.3" }, "funding": { @@ -667,14 +619,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.0.tgz", - "integrity": "sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz", + "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.0", - "@vitest/utils": "4.1.0", + "@vitest/pretty-format": "4.1.11", + "@vitest/utils": "4.1.11", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -683,9 +635,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.0.tgz", - "integrity": "sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz", + "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", "dev": true, "license": "MIT", "funding": { @@ -693,15 +645,15 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.0.tgz", - "integrity": "sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz", + "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.0", + "@vitest/pretty-format": "4.1.11", "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.0.3" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -725,9 +677,9 @@ "license": "Apache-2.0" }, "node_modules/chai": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", - "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.3.0.tgz", + "integrity": "sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==", "dev": true, "license": "MIT", "engines": { @@ -836,9 +788,9 @@ "license": "MIT" }, "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -852,23 +804,23 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" } }, "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", "cpu": [ "arm64" ], @@ -887,9 +839,9 @@ } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", "cpu": [ "arm64" ], @@ -908,9 +860,9 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", "cpu": [ "x64" ], @@ -929,9 +881,9 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", "cpu": [ "x64" ], @@ -950,9 +902,9 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", "cpu": [ "arm" ], @@ -971,13 +923,16 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -992,13 +947,16 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1013,13 +971,16 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1034,13 +995,16 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1055,9 +1019,9 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", "cpu": [ "arm64" ], @@ -1076,9 +1040,9 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", "cpu": [ "x64" ], @@ -1107,9 +1071,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.17", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz", - "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==", + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", "dev": true, "funding": [ { @@ -1151,9 +1115,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "engines": { @@ -1164,9 +1128,9 @@ } }, "node_modules/postcss": { - "version": "8.5.25", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", - "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", + "version": "8.5.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz", + "integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==", "dev": true, "funding": [ { @@ -1184,22 +1148,22 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.16", + "nanoid": "^3.3.19", "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "source-map-js": "^1.2.2" }, "engines": { "node": "^10 || ^12 || >=14" } }, "node_modules/rolldown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", - "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.13.tgz", + "integrity": "sha512-/5apOkwWxtACcQtZ5WlrAsKS4JwXVThQPJZ3RqIruRY9UG9M8e0VlXN068xum/JRKA4aIUoJ13ghe/kGCvidiQ==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.133.0", + "@oxc-project/types": "=0.153.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -1209,21 +1173,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.3", - "@rolldown/binding-darwin-arm64": "1.0.3", - "@rolldown/binding-darwin-x64": "1.0.3", - "@rolldown/binding-freebsd-x64": "1.0.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", - "@rolldown/binding-linux-arm64-gnu": "1.0.3", - "@rolldown/binding-linux-arm64-musl": "1.0.3", - "@rolldown/binding-linux-ppc64-gnu": "1.0.3", - "@rolldown/binding-linux-s390x-gnu": "1.0.3", - "@rolldown/binding-linux-x64-gnu": "1.0.3", - "@rolldown/binding-linux-x64-musl": "1.0.3", - "@rolldown/binding-openharmony-arm64": "1.0.3", - "@rolldown/binding-wasm32-wasi": "1.0.3", - "@rolldown/binding-win32-arm64-msvc": "1.0.3", - "@rolldown/binding-win32-x64-msvc": "1.0.3" + "@rolldown/binding-android-arm-eabi": "1.2.13", + "@rolldown/binding-android-arm64": "1.2.13", + "@rolldown/binding-darwin-arm64": "1.2.13", + "@rolldown/binding-darwin-x64": "1.2.13", + "@rolldown/binding-freebsd-x64": "1.2.13", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.13", + "@rolldown/binding-linux-arm64-gnu": "1.2.13", + "@rolldown/binding-linux-arm64-musl": "1.2.13", + "@rolldown/binding-linux-ppc64-gnu": "1.2.13", + "@rolldown/binding-linux-s390x-gnu": "1.2.13", + "@rolldown/binding-linux-x64-gnu": "1.2.13", + "@rolldown/binding-linux-x64-musl": "1.2.13", + "@rolldown/binding-openharmony-arm64": "1.2.13", + "@rolldown/binding-win32-arm64-msvc": "1.2.13", + "@rolldown/binding-win32-x64-msvc": "1.2.13" } }, "node_modules/siginfo": { @@ -1234,9 +1198,9 @@ "license": "ISC" }, "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -1292,23 +1256,15 @@ } }, "node_modules/tinyrainbow": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", - "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.2.0.tgz", + "integrity": "sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==", "dev": true, "license": "MIT", "engines": { "node": ">=14.0.0" } }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD", - "optional": true - }, "node_modules/tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", @@ -1358,16 +1314,16 @@ "license": "ISC" }, "node_modules/vite": { - "version": "8.0.16", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", - "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.4.tgz", + "integrity": "sha512-IftLE8phJhdNRIGNEV4JCCvfNScoZQVfcXaN8TQropWGJtOHcu+iAB9pHcuGvmEb/kddw0LBe/jS8ztdC6xdiQ==", "dev": true, "license": "MIT", "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.15", - "rolldown": "1.0.3", + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.29", + "rolldown": "~1.2.12", "tinyglobby": "^0.2.17" }, "bin": { @@ -1384,7 +1340,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.18", + "@vitejs/devtools": "^0.7.1", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -1436,19 +1392,19 @@ } }, "node_modules/vitest": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.0.tgz", - "integrity": "sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz", + "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.0", - "@vitest/mocker": "4.1.0", - "@vitest/pretty-format": "4.1.0", - "@vitest/runner": "4.1.0", - "@vitest/snapshot": "4.1.0", - "@vitest/spy": "4.1.0", - "@vitest/utils": "4.1.0", + "@vitest/expect": "4.1.11", + "@vitest/mocker": "4.1.11", + "@vitest/pretty-format": "4.1.11", + "@vitest/runner": "4.1.11", + "@vitest/snapshot": "4.1.11", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -1459,8 +1415,8 @@ "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.0.3", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "bin": { @@ -1476,13 +1432,15 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.0", - "@vitest/browser-preview": "4.1.0", - "@vitest/browser-webdriverio": "4.1.0", - "@vitest/ui": "4.1.0", + "@vitest/browser-playwright": "4.1.11", + "@vitest/browser-preview": "4.1.11", + "@vitest/browser-webdriverio": "4.1.11", + "@vitest/coverage-istanbul": "4.1.11", + "@vitest/coverage-v8": "4.1.11", + "@vitest/ui": "4.1.11", "happy-dom": "*", "jsdom": "*", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { @@ -1503,6 +1461,12 @@ "@vitest/browser-webdriverio": { "optional": true }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, "@vitest/ui": { "optional": true }, diff --git a/scripts/corrections/package.json b/scripts/corrections/package.json index 0afee52cc9..0dc9cfeb4b 100644 --- a/scripts/corrections/package.json +++ b/scripts/corrections/package.json @@ -10,6 +10,6 @@ "@octokit/rest": "^22.0.1", "@types/node": "^22.0.0", "typescript": "^5.8.0", - "vitest": "^4.1.0" + "vitest": "^4.1.11" } } diff --git a/scripts/docs-validation/package-lock.json b/scripts/docs-validation/package-lock.json index 0c2751fa78..0f0c48a705 100644 --- a/scripts/docs-validation/package-lock.json +++ b/scripts/docs-validation/package-lock.json @@ -480,9 +480,9 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", - "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json index 0c31f6ea1d..8a72836f53 100644 --- a/test/harness/package-lock.json +++ b/test/harness/package-lock.json @@ -9,51 +9,20 @@ "version": "1.0.0", "license": "ISC", "devDependencies": { - "@modelcontextprotocol/sdk": "^1.26.0", + "@modelcontextprotocol/sdk": "^1.31.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", "node-forge": "^1.4.0", "openai": "^6.17.0", "tsx": "^4.21.0", "typescript": "^5.9.3", - "vitest": "^4.0.18", + "vitest": "^4.1.11", "yaml": "^2.8.2" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@emnapi/core": { - "version": "1.10.0", - "integrity": "sha1-OAzMjyQS6iLR2XLff47iOjucdGc=", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "integrity": "sha1-SyYMDTU0IE6YxhELjbGph9JuyHw=", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "integrity": "sha1-KP7SGhuhznl8RKBwq8lNQvOuhUg=", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@esbuild/aix-ppc64": { "version": "0.28.1", "integrity": "sha1-egGo0uwvuy2seK2tCbD6eB5Agr4=", @@ -469,28 +438,19 @@ "node": ">=18" } }, - "node_modules/@hono/node-server": { - "version": "1.19.14", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.14.1" - }, - "peerDependencies": { - "hono": "^4" - } - }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", + "version": "1.6.0", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", "dev": true, "license": "MIT" }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.26.0", + "version": "1.32.1", + "integrity": "sha512-2DdE+SJDtzLEEWzY1ZjY7Q+VcPhcV1KisD3zI4u0XZyktsjHum1mwbMI+JaulUBi2OZk+KJAi2uPXzxichPkdw==", "dev": true, "license": "MIT", "dependencies": { - "@hono/node-server": "^1.19.9", + "@hono/node-server": "^1.19.9 || ^2.0.5", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", @@ -524,38 +484,46 @@ } } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.2.3", - "integrity": "sha1-l+PUXXQk3F2h1OMvO/OykvbBtEw=", + "node_modules/@modelcontextprotocol/sdk/node_modules/@hono/node-server": { + "version": "1.19.17", + "integrity": "sha512-dSneS5qhiauZWGDCeK4o695Xd9nUNjviSZCMQrj10eetr8Uln1ucn6bbphOM6UynAMMtNIzZNSpL9vnASJwrPQ==", "dev": true, "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.3" - }, "engines": { - "node": "^20.19.0 || ^22.13.0 || >=23.5.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" + "node": ">=18.14.1" }, "peerDependencies": { - "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", - "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" + "hono": "^4" } }, "node_modules/@oxc-project/types": { - "version": "0.133.0", + "version": "0.153.0", + "integrity": "sha512-jSNrRIyvPVLL5E1z/tS5zLAprosGz/FuI1PWwEOz59j3E4TTTzSpqKb/RDGqJhhW0QHYISGXxq582wGx6T11Tw==", "dev": true, "license": "MIT", "funding": { - "url": "https://github.com/sponsors/Boshen" + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.13", + "integrity": "sha512-Vu/NICrgdRvUOeaJZIyB2FP63s6fO/4ud5l8dXmxDx9yDyn+mN0y+gsvsVviSli+T361Aqo0h46EuMoobrwawA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.3", - "integrity": "sha1-VM6Pg4IhP0oxSgwve6g/gf/q5ZI=", + "version": "1.2.13", + "integrity": "sha512-6LEoIrGy5vvCd2boNnv2soPSUZj/PRvhp8R/t9KlX+mIZxThIE/ZqxrGXxmuNX0FuvQFvLdZr7S4HrYpYBVhEQ==", "cpu": [ "arm64" ], @@ -570,7 +538,8 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.3", + "version": "1.2.13", + "integrity": "sha512-CaJSRzlQ6Q+zkDMVA9OkVxBjVoPRCDEE3v0FSJ9kIQBgRjFApA3Si4ijhbNVUji7ZJt+PfO0e46qy4nPInHUPA==", "cpu": [ "arm64" ], @@ -585,8 +554,8 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.3", - "integrity": "sha1-U/V94fWZ7PHbE4I8/IjBj7gJVK0=", + "version": "1.2.13", + "integrity": "sha512-e4r340I5hxSzJkD3YJBxgzq/rJ3wwxwdLLb2FPt1Zpyem20nrQPT11DDEEkazwxaHumH8wpglsp/Ozgj/wT6mw==", "cpu": [ "x64" ], @@ -601,8 +570,8 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.3", - "integrity": "sha1-bz/dobeuqsnSaKUmgEtPuW5ONfE=", + "version": "1.2.13", + "integrity": "sha512-hoBWDZ4/K/mJKVMbsgUd1jfpA2DiOrJ54mpegObU9TBHBBrtX4FLIXJJQRDPZ/2Dn91GvPob+Z2hwKJzf4E5SQ==", "cpu": [ "x64" ], @@ -617,8 +586,8 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.3", - "integrity": "sha1-2HpFS/WFzJZ2hJN36R1uN1KXMm8=", + "version": "1.2.13", + "integrity": "sha512-oF9fZu3vTWnTiVAkDFxU9PJLt1nh20K9RRpuHsR7ZnE2L7XookBbDBVLojCVSLGbvtSlxeUNnjAj6DbNsKyzbg==", "cpu": [ "arm" ], @@ -633,12 +602,15 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.3", - "integrity": "sha1-QZ/Wv2Es80jxBSjLzZTrq5YH2NE=", + "version": "1.2.13", + "integrity": "sha512-DTpb/+jMiqWMPgphn4ksW1zW+XLJ6dJJ9FMvnIk9I291LhCctMPhMYJrP5t9p6C9qa3KXadDdUEZHBaPh3kNxQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -649,12 +621,15 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.3", - "integrity": "sha1-/MaRhpa7doRId+HkkwoY/Q03QGk=", + "version": "1.2.13", + "integrity": "sha512-xU+Kvv9N2A3SY35zFh1FWWNjxbadE/BKeqnkXe7vScU4GZsw757BlVmDLITZEq8DPpk6m7uoDYkK7P+x/uU5Ow==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -665,12 +640,15 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.3", - "integrity": "sha1-Mq7LfI2uXU8qjN5XoFjshpkVQvg=", + "version": "1.2.13", + "integrity": "sha512-XMk/hnhh25oQ756Lclb1Q11h4ll0xC0Esa5w4Vzp+mAE2S3AGJgeM0SonrIgY8fHhjarb6hcoTuwoFG0VVP2ig==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -681,12 +659,15 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.3", - "integrity": "sha1-vtk0bqgea7i5PPEfXYi3fbiQt2M=", + "version": "1.2.13", + "integrity": "sha512-U1BoTHYmlywE2n3HUWjzZ+8EQsKr+OwpxYuDWaubx42tUL8QSL7nOqRz/NVHanibbFP3M1Rxuprz/wNcbMKjqw==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -697,12 +678,15 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.3", - "integrity": "sha1-ZMLSb3Xf/ZtaH5dVegCudyUMjLc=", + "version": "1.2.13", + "integrity": "sha512-J4cm80Pc6wEwV5AZ42o62sIQgDk+Y7R2s646jIwuvO+4V46QB58N4vVuMPWCBaNgursXLpmUWJV//WQAkPvHBQ==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -713,12 +697,15 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.3", - "integrity": "sha1-WkUTLopHZZ7qrztUDClUqXyGD/M=", + "version": "1.2.13", + "integrity": "sha512-W0euZS40U4V6JkmEnVCIX9mcsL6cbXQ4X8c2w+MMOIjFg68TXUW+u3RT+2e1t1pv9mg/EP3pDtK3POXdQ7Rxyw==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -729,8 +716,8 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.3", - "integrity": "sha1-KQUTBoxV6EnchFejKv7h17Csswk=", + "version": "1.2.13", + "integrity": "sha512-do/AF/4n9MRshia3a5aXoy8xM2c6mgfT6D7H/dY6my5z+hfCIR4ab7mk4IRj++nsEQpKy39kd0DQnDq+r692tw==", "cpu": [ "arm64" ], @@ -744,27 +731,9 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.3", - "integrity": "sha1-PZly2/GpU9PHr6pKDyDvKy458xs=", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.3", - "integrity": "sha1-oASrYHoW1vA7y1VXKP+IivdXc60=", + "version": "1.2.13", + "integrity": "sha512-6FtUf2cJZobutJdxnGYX8z0vtbgqU0b2v8vfBhkXb9wp2MppMFtkunE+IaOntyi01rkIQoPW2GHABtfzsC0BRQ==", "cpu": [ "arm64" ], @@ -779,8 +748,8 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.3", - "integrity": "sha1-4qJbNGkaHMihIJ195wkGMCbdDNs=", + "version": "1.2.13", + "integrity": "sha512-UKhFaZTeTcbKrb3iU82m17jVcPEAoqDkCN5MBqgPyZSwp/cQmib21crTS0DdH7T1B7wZRWEI89YQAOJMet5yhg==", "cpu": [ "x64" ], @@ -796,26 +765,19 @@ }, "node_modules/@rolldown/pluginutils": { "version": "1.0.1", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, "license": "MIT" }, "node_modules/@standard-schema/spec": { "version": "1.1.0", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "dev": true, "license": "MIT" }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.3", - "integrity": "sha1-AVy6np3UfOFNA9KoxdVHv7FpZl0=", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/chai": { "version": "5.2.3", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", "dev": true, "license": "MIT", "dependencies": { @@ -825,11 +787,13 @@ }, "node_modules/@types/deep-eql": { "version": "4.0.2", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", "dev": true, "license": "MIT" }, "node_modules/@types/estree": { "version": "1.0.9", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "dev": true, "license": "MIT" }, @@ -850,14 +814,15 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.8", + "version": "4.1.11", + "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.8", - "@vitest/utils": "4.1.8", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -866,11 +831,12 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.8", + "version": "4.1.11", + "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.8", + "@vitest/spy": "4.1.11", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -891,7 +857,8 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.8", + "version": "4.1.11", + "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==", "dev": true, "license": "MIT", "dependencies": { @@ -902,11 +869,12 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.8", + "version": "4.1.11", + "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.8", + "@vitest/utils": "4.1.11", "pathe": "^2.0.3" }, "funding": { @@ -914,12 +882,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.8", + "version": "4.1.11", + "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.8", - "@vitest/utils": "4.1.8", + "@vitest/pretty-format": "4.1.11", + "@vitest/utils": "4.1.11", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -928,7 +897,8 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.8", + "version": "4.1.11", + "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", "dev": true, "license": "MIT", "funding": { @@ -936,11 +906,12 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.8", + "version": "4.1.11", + "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.8", + "@vitest/pretty-format": "4.1.11", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -993,6 +964,7 @@ }, "node_modules/assertion-error": { "version": "2.0.1", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", "engines": { @@ -1000,19 +972,20 @@ } }, "node_modules/body-parser": { - "version": "2.2.2", + "version": "2.3.0", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", "dev": true, "license": "MIT", "dependencies": { "bytes": "^3.1.2", - "content-type": "^1.0.5", + "content-type": "^2.0.0", "debug": "^4.4.3", - "http-errors": "^2.0.0", - "iconv-lite": "^0.7.0", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", - "qs": "^6.14.1", - "raw-body": "^3.0.1", - "type-is": "^2.0.1" + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" }, "engines": { "node": ">=18" @@ -1022,6 +995,19 @@ "url": "https://opencollective.com/express" } }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.1.0", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/bytes": { "version": "3.1.2", "dev": true, @@ -1032,6 +1018,7 @@ }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1044,6 +1031,7 @@ }, "node_modules/call-bound": { "version": "1.0.4", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "dev": true, "license": "MIT", "dependencies": { @@ -1058,7 +1046,8 @@ } }, "node_modules/chai": { - "version": "6.2.2", + "version": "6.3.0", + "integrity": "sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==", "dev": true, "license": "MIT", "engines": { @@ -1087,6 +1076,7 @@ }, "node_modules/convert-source-map": { "version": "2.0.0", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, "license": "MIT" }, @@ -1161,6 +1151,7 @@ }, "node_modules/detect-libc": { "version": "2.1.2", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1169,6 +1160,7 @@ }, "node_modules/dunder-proto": { "version": "1.0.1", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dev": true, "license": "MIT", "dependencies": { @@ -1195,6 +1187,7 @@ }, "node_modules/es-define-property": { "version": "1.0.1", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, "license": "MIT", "engines": { @@ -1203,6 +1196,7 @@ }, "node_modules/es-errors": { "version": "1.3.0", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, "license": "MIT", "engines": { @@ -1215,7 +1209,8 @@ "license": "MIT" }, "node_modules/es-object-atoms": { - "version": "1.1.1", + "version": "1.1.2", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "dev": true, "license": "MIT", "dependencies": { @@ -1272,6 +1267,7 @@ }, "node_modules/estree-walker": { "version": "3.0.3", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, "license": "MIT", "dependencies": { @@ -1378,8 +1374,8 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.7", - "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", + "version": "3.1.8", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", "dev": true, "funding": [ { @@ -1395,6 +1391,7 @@ }, "node_modules/fdir": { "version": "6.5.0", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", "engines": { @@ -1459,6 +1456,7 @@ }, "node_modules/function-bind": { "version": "1.1.2", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, "license": "MIT", "funding": { @@ -1467,6 +1465,7 @@ }, "node_modules/get-intrinsic": { "version": "1.3.0", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1490,6 +1489,7 @@ }, "node_modules/get-proto": { "version": "1.0.1", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "dev": true, "license": "MIT", "dependencies": { @@ -1502,6 +1502,7 @@ }, "node_modules/gopd": { "version": "1.2.0", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, "license": "MIT", "engines": { @@ -1513,6 +1514,7 @@ }, "node_modules/has-symbols": { "version": "1.1.0", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, "license": "MIT", "engines": { @@ -1523,7 +1525,8 @@ } }, "node_modules/hasown": { - "version": "2.0.2", + "version": "2.0.4", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "dev": true, "license": "MIT", "dependencies": { @@ -1582,7 +1585,8 @@ "license": "ISC" }, "node_modules/ip-address": { - "version": "10.4.0", + "version": "10.7.4", + "integrity": "sha512-DWBMKyeM25D70nrIiTkgQth59/G+naKOTep6huF9j+GmeK3DMD60Zvxg5d5qQnz+38AHgou6ean2LECX+lrKDg==", "dev": true, "license": "MIT", "engines": { @@ -1626,7 +1630,8 @@ "license": "BSD-2-Clause" }, "node_modules/lightningcss": { - "version": "1.32.0", + "version": "1.33.0", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -1640,22 +1645,22 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" } }, "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "integrity": "sha1-8DOIURbf79nG9UeHUj41FLYeGWg=", + "version": "1.33.0", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", "cpu": [ "arm64" ], @@ -1674,7 +1679,8 @@ } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", + "version": "1.33.0", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", "cpu": [ "arm64" ], @@ -1693,8 +1699,8 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "integrity": "sha1-NfPpczLRMLnKGB4RtWje1q68bV4=", + "version": "1.33.0", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", "cpu": [ "x64" ], @@ -1713,8 +1719,8 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "integrity": "sha1-l3enZHK2Ttb/lDQq1kx7r9eUpXU=", + "version": "1.33.0", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", "cpu": [ "x64" ], @@ -1733,8 +1739,8 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "integrity": "sha1-E65lLhq3O5E117faFy9mbEEK1T0=", + "version": "1.33.0", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", "cpu": [ "arm" ], @@ -1753,12 +1759,15 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "integrity": "sha1-QXhYeVqUWS9oASOhsfnaig4e8zU=", + "version": "1.33.0", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1773,12 +1782,15 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "integrity": "sha1-a+NmkugQtxgECAL9gJYjz/5zITM=", + "version": "1.33.0", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1793,12 +1805,15 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "integrity": "sha1-C3gDr06yHP043Tn+Kru1PH3QkfY=", + "version": "1.33.0", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1813,12 +1828,15 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "integrity": "sha1-iNyLqGXd3bGsXvBLDxYYBEGMFjs=", + "version": "1.33.0", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1833,8 +1851,8 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "integrity": "sha1-TzC6P6XpJfW3n5RejMDRdsOxqzg=", + "version": "1.33.0", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", "cpu": [ "arm64" ], @@ -1853,8 +1871,8 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "integrity": "sha1-FBqlYFZFBkkokCu0rwRfp9n0Igo=", + "version": "1.33.0", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", "cpu": [ "x64" ], @@ -1874,6 +1892,7 @@ }, "node_modules/magic-string": { "version": "0.30.21", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1882,6 +1901,7 @@ }, "node_modules/math-intrinsics": { "version": "1.1.0", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true, "license": "MIT", "engines": { @@ -1936,7 +1956,8 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.17", + "version": "3.3.20", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", "dev": true, "funding": [ { @@ -1978,6 +1999,7 @@ }, "node_modules/object-inspect": { "version": "1.13.4", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, "license": "MIT", "engines": { @@ -2062,16 +2084,19 @@ }, "node_modules/pathe": { "version": "2.0.3", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, "license": "MIT" }, "node_modules/picocolors": { "version": "1.1.1", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.4", + "version": "4.0.7", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "engines": { @@ -2090,7 +2115,8 @@ } }, "node_modules/postcss": { - "version": "8.5.25", + "version": "8.5.29", + "integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==", "dev": true, "funding": [ { @@ -2108,16 +2134,17 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.16", + "nanoid": "^3.3.19", "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "source-map-js": "^1.2.2" }, "engines": { "node": "^10 || ^12 || >=14" } }, "node_modules/proxy-addr": { - "version": "2.0.7", + "version": "2.0.8", + "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2126,14 +2153,20 @@ }, "engines": { "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/qs": { - "version": "6.15.2", + "version": "6.16.0", + "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.1.0" + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" }, "engines": { "node": ">=0.6" @@ -2173,11 +2206,12 @@ } }, "node_modules/rolldown": { - "version": "1.0.3", + "version": "1.2.13", + "integrity": "sha512-/5apOkwWxtACcQtZ5WlrAsKS4JwXVThQPJZ3RqIruRY9UG9M8e0VlXN068xum/JRKA4aIUoJ13ghe/kGCvidiQ==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.133.0", + "@oxc-project/types": "=0.153.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -2187,21 +2221,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.3", - "@rolldown/binding-darwin-arm64": "1.0.3", - "@rolldown/binding-darwin-x64": "1.0.3", - "@rolldown/binding-freebsd-x64": "1.0.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", - "@rolldown/binding-linux-arm64-gnu": "1.0.3", - "@rolldown/binding-linux-arm64-musl": "1.0.3", - "@rolldown/binding-linux-ppc64-gnu": "1.0.3", - "@rolldown/binding-linux-s390x-gnu": "1.0.3", - "@rolldown/binding-linux-x64-gnu": "1.0.3", - "@rolldown/binding-linux-x64-musl": "1.0.3", - "@rolldown/binding-openharmony-arm64": "1.0.3", - "@rolldown/binding-wasm32-wasi": "1.0.3", - "@rolldown/binding-win32-arm64-msvc": "1.0.3", - "@rolldown/binding-win32-x64-msvc": "1.0.3" + "@rolldown/binding-android-arm-eabi": "1.2.13", + "@rolldown/binding-android-arm64": "1.2.13", + "@rolldown/binding-darwin-arm64": "1.2.13", + "@rolldown/binding-darwin-x64": "1.2.13", + "@rolldown/binding-freebsd-x64": "1.2.13", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.13", + "@rolldown/binding-linux-arm64-gnu": "1.2.13", + "@rolldown/binding-linux-arm64-musl": "1.2.13", + "@rolldown/binding-linux-ppc64-gnu": "1.2.13", + "@rolldown/binding-linux-s390x-gnu": "1.2.13", + "@rolldown/binding-linux-x64-gnu": "1.2.13", + "@rolldown/binding-linux-x64-musl": "1.2.13", + "@rolldown/binding-openharmony-arm64": "1.2.13", + "@rolldown/binding-win32-arm64-msvc": "1.2.13", + "@rolldown/binding-win32-x64-msvc": "1.2.13" } }, "node_modules/router": { @@ -2292,13 +2326,14 @@ } }, "node_modules/side-channel": { - "version": "1.1.0", + "version": "1.1.1", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" }, @@ -2310,12 +2345,13 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", + "version": "1.0.1", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -2326,6 +2362,7 @@ }, "node_modules/side-channel-map": { "version": "1.0.1", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "dev": true, "license": "MIT", "dependencies": { @@ -2343,6 +2380,7 @@ }, "node_modules/side-channel-weakmap": { "version": "1.0.2", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dev": true, "license": "MIT", "dependencies": { @@ -2365,7 +2403,8 @@ "license": "ISC" }, "node_modules/source-map-js": { - "version": "1.2.1", + "version": "1.2.2", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -2405,6 +2444,7 @@ }, "node_modules/tinyglobby": { "version": "0.2.17", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", "dependencies": { @@ -2419,7 +2459,8 @@ } }, "node_modules/tinyrainbow": { - "version": "3.1.0", + "version": "3.2.0", + "integrity": "sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==", "dev": true, "license": "MIT", "engines": { @@ -2434,13 +2475,6 @@ "node": ">=0.6" } }, - "node_modules/tslib": { - "version": "2.8.1", - "integrity": "sha1-YS7+TtI11Wfoq6Xypfq3AoCt6D8=", - "dev": true, - "license": "0BSD", - "optional": true - }, "node_modules/tsx": { "version": "4.22.4", "dev": true, @@ -2459,16 +2493,34 @@ } }, "node_modules/type-is": { - "version": "2.0.1", + "version": "2.1.0", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", "dev": true, "license": "MIT", "dependencies": { - "content-type": "^1.0.5", + "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.1.0", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/typescript": { @@ -2505,14 +2557,15 @@ } }, "node_modules/vite": { - "version": "8.0.16", + "version": "8.3.4", + "integrity": "sha512-IftLE8phJhdNRIGNEV4JCCvfNScoZQVfcXaN8TQropWGJtOHcu+iAB9pHcuGvmEb/kddw0LBe/jS8ztdC6xdiQ==", "dev": true, "license": "MIT", "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.15", - "rolldown": "1.0.3", + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.29", + "rolldown": "~1.2.12", "tinyglobby": "^0.2.17" }, "bin": { @@ -2529,7 +2582,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.18", + "@vitejs/devtools": "^0.7.1", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -2581,17 +2634,18 @@ } }, "node_modules/vitest": { - "version": "4.1.8", + "version": "4.1.11", + "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.8", - "@vitest/mocker": "4.1.8", - "@vitest/pretty-format": "4.1.8", - "@vitest/runner": "4.1.8", - "@vitest/snapshot": "4.1.8", - "@vitest/spy": "4.1.8", - "@vitest/utils": "4.1.8", + "@vitest/expect": "4.1.11", + "@vitest/mocker": "4.1.11", + "@vitest/pretty-format": "4.1.11", + "@vitest/runner": "4.1.11", + "@vitest/snapshot": "4.1.11", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -2619,12 +2673,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.8", - "@vitest/browser-preview": "4.1.8", - "@vitest/browser-webdriverio": "4.1.8", - "@vitest/coverage-istanbul": "4.1.8", - "@vitest/coverage-v8": "4.1.8", - "@vitest/ui": "4.1.8", + "@vitest/browser-playwright": "4.1.11", + "@vitest/browser-preview": "4.1.11", + "@vitest/browser-webdriverio": "4.1.11", + "@vitest/coverage-istanbul": "4.1.11", + "@vitest/coverage-v8": "4.1.11", + "@vitest/ui": "4.1.11", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" diff --git a/test/harness/package.json b/test/harness/package.json index 9b37dfb9d0..055121c48e 100644 --- a/test/harness/package.json +++ b/test/harness/package.json @@ -14,14 +14,17 @@ "node": "^20.19.0 || >=22.12.0" }, "devDependencies": { - "@modelcontextprotocol/sdk": "^1.26.0", + "@modelcontextprotocol/sdk": "^1.31.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", "node-forge": "^1.4.0", "openai": "^6.17.0", "tsx": "^4.21.0", "typescript": "^5.9.3", - "vitest": "^4.0.18", + "vitest": "^4.1.11", "yaml": "^2.8.2" + }, + "overrides": { + "@hono/node-server": "^1.19.15" } } diff --git a/test/harness/replayingCapiProxy.test.ts b/test/harness/replayingCapiProxy.test.ts index cbd51c1b09..347b2b9547 100644 --- a/test/harness/replayingCapiProxy.test.ts +++ b/test/harness/replayingCapiProxy.test.ts @@ -11,6 +11,7 @@ import type { } from "openai/resources/chat/completions"; import os from "os"; import path from "path"; +import { gzipSync } from "zlib"; import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; import yaml from "yaml"; import { @@ -148,6 +149,31 @@ describe("ReplayingCapiProxy", () => { } }); + test("accepts telemetry locally before replay configuration", async () => { + const proxy = new ReplayingCapiProxy("http://localhost"); + const address = await proxy.start(); + try { + const response = await fetch(`${address}/telemetry`, { + method: "POST", + headers: { + "content-type": "application/x-json-stream", + "content-encoding": "gzip", + }, + body: gzipSync('{"name":"fixture-event"}\n'), + }); + expect(response.status).toBe(204); + expect(await response.text()).toBe(""); + expect(proxy.exchanges).toMatchObject([ + { + request: { url: "/telemetry", method: "POST" }, + response: { statusCode: 204, body: "" }, + }, + ]); + } finally { + await proxy.stop(); + } + }); + test("does not write file when no chat completion exchanges", async () => { const outputPath = path.join(tempDir, "output.yaml"); const proxy = new ReplayingCapiProxy( diff --git a/test/harness/replayingCapiProxy.ts b/test/harness/replayingCapiProxy.ts index ad21f0d317..b4e0f455a5 100644 --- a/test/harness/replayingCapiProxy.ts +++ b/test/harness/replayingCapiProxy.ts @@ -352,6 +352,15 @@ export class ReplayingCapiProxy extends CapturingHttpProxy { return; } + if ( + options.requestOptions.path === "/telemetry" && + options.requestOptions.method === "POST" + ) { + options.onResponseStart(204, {}); + options.onResponseEnd(); + return; + } + if ( options.requestOptions.path === "/memory-api-config" && options.requestOptions.method === "POST" diff --git a/test/harness/sdk-diagnostics.mjs b/test/harness/sdk-diagnostics.mjs index 817df78c39..829c8ed27d 100644 --- a/test/harness/sdk-diagnostics.mjs +++ b/test/harness/sdk-diagnostics.mjs @@ -46,15 +46,62 @@ const shellState = { }; const records = { "workflow availability decision": { available: "boolean" }, + "sdk transport teardown": { + connection: "count", + active_requests: "count", + driver_finished: "boolean", + phase: [ + "shutdown_writer", + "launcher", + "launcher_complete", + "driver", + "driver_complete", + "driver_failed", + "driver_timeout", + "driver_abort_complete", + "writer_shutdown_complete", + "peer_eof", + "notification_worker", + "notification_worker_complete", + "notification_worker_failed", + "notification_worker_timed_out", + "active_request_grace", + "handle_close", + "handle_close_complete", + "active_requests", + "active_requests_complete", + "active_requests_timed_out", + "flush", + "flush_complete", + "flush_cancelled", + "flush_timed_out", + "flush_unavailable", + "writer_join", + "writer_join_complete", + "complete", + ], + }, "sdk pending permission response": { ...presence, prepared_replay: "boolean", aborting: "boolean", - reason: ["prepared_replay", "completed_request", "unknown_request", "resolved_tool", "accepted"], + reason: [ + "prepared_replay", + "completed_request", + "unknown_request", + "resolved_tool", + "accepted", + ], }, "sdk pending tool response": { ...presence, - reason: ["completed_request", "unknown_request", "non_pending_request", "completed_tool", "accepted"], + reason: [ + "completed_request", + "unknown_request", + "non_pending_request", + "completed_tool", + "accepted", + ], }, "sdk pending permission delivery": { generation: "number", @@ -163,6 +210,105 @@ const records = { child_span_handle: "number", chat_span_handle: "number", }, + "sdk otel admission": { + phase: ["received", "no_tracker", "fenced", "pending", "submitted"], + event_sequence: "count", + policy_revision: "count", + authority_fenced: "boolean", + preparation_pending: "boolean", + policy_enabled: "boolean", + projected_event_count: "count", + submitted: "boolean", + }, + "sdk otel policy state": { + origin: [ + "host_prepare", + "host_commit", + "host_commit_initial", + "host_bind", + "host_rollback", + "explicit_fence", + "legacy_settled", + "native_pending", + "native_settled", + "native_apply", + "reserved_apply", + "direct_apply", + ], + kind: [ + "transition", + "restore", + "fence", + "apply_stale_source", + "apply_retry", + "apply_superseded", + "apply_settled", + ], + expected_some: "boolean", + expected_incarnation: "count", + expected_revision: "count", + previous_incarnation: "count", + previous_revision: "count", + current_incarnation: "count", + current_revision: "count", + previous_authority_fenced: "boolean", + authority_fenced: "boolean", + previous_preparation_pending: "boolean", + preparation_pending: "boolean", + previous_native_owned: "boolean", + native_owned: "boolean", + policy_enabled: "boolean", + returned_some: "boolean", + applied: "boolean", + build_succeeded: "boolean", + }, + "sdk otel core policy publication": { + origin: [ + "clear_account_policy", + "retained_device_update", + "retained_server_update", + "retained_signed_out_update", + "registry_rotation", + "signed_out_policy_clear", + "refresh_start", + "ingest_settlement", + "standalone_provider_rebind", + "standalone_provider_restore", + "construction_options_auth", + "manager_auth_changed", + "resolved_session_auth", + "hydrated_session_auth", + "set_credentials", + "clear_credentials", + "other_auth_mutation", + ], + kind: ["pending_publish", "pending_publish_returned"], + registry_generation: "count", + previous_auth_generation: "count", + auth_generation: "count", + auth_generation_changed: "boolean", + previous_server_pending: "boolean", + server_pending: "boolean", + policy_identity_present: "boolean", + source_recovery: "boolean", + clear_retained: "boolean", + composition_succeeded: "boolean", + refresh_generation: "count", + has_resolved_managed_settings: "boolean", + has_eligible_current_otel_policy: "boolean", + expected_auth_generation: "count", + }, + "sdk otel policy settlement": { + origin: ["standalone_commit"], + kind: ["reservation", "application"], + succeeded: "boolean", + reservation_some: "boolean", + committed_incarnation: "count", + committed_revision: "count", + expected_incarnation: "count", + expected_revision: "count", + applied: "boolean", + }, "sdk assistant message": { ...childParts, scope: ["parent", "child"], @@ -174,7 +320,12 @@ const records = { ...childParts, scope: ["parent", "child"], operation: ["chat"], - reason: [...childCloseReasons, "model_call_start", "assistant_turn_end", "parent_turn_close"], + reason: [ + ...childCloseReasons, + "model_call_start", + "assistant_turn_end", + "parent_turn_close", + ], status: ["unset", "error"], }, "sdk child completion": { @@ -230,7 +381,14 @@ const records = { }, "sdk shell wait": { ...shellState, - phase: ["interrupted", "stale_generation", "completion", "cancelled", "deadline", "completion_after_deadline"], + phase: [ + "interrupted", + "stale_generation", + "completion", + "cancelled", + "deadline", + "completion_after_deadline", + ], wait_generation: "number", current_wait_generation: "number", elapsed_ms: "elapsed", @@ -257,9 +415,11 @@ export function sanitizeDiagnosticLine(line) { ? rule.includes(value) : rule === "number" ? Number.isSafeInteger(value) - : rule === "elapsed" - ? Number.isFinite(value) && value >= 0 - : typeof value === rule; + : rule === "count" + ? Number.isSafeInteger(value) && value >= 0 + : rule === "elapsed" + ? Number.isFinite(value) && value >= 0 + : typeof value === rule; if (!valid) return undefined; output[key] = value; } @@ -285,7 +445,10 @@ export async function readDiagnosticTail(home) { } for (const file of files.sort()) { if (!file.endsWith(".log")) continue; - const lines = createInterface({ input: createReadStream(join(home, "logs", file)), crlfDelay: Infinity }); + const lines = createInterface({ + input: createReadStream(join(home, "logs", file)), + crlfDelay: Infinity, + }); for await (const line of lines) append(line); } return tail; diff --git a/test/harness/sdk-diagnostics.test.ts b/test/harness/sdk-diagnostics.test.ts index 53d372a284..9a57c8b8d9 100644 --- a/test/harness/sdk-diagnostics.test.ts +++ b/test/harness/sdk-diagnostics.test.ts @@ -11,6 +11,251 @@ import { readDiagnosticTail, sanitizeDiagnosticLine } from "./sdk-diagnostics.mj const prefix = "2026-10-05T12:00:00Z [DEBUG] [rust:sdk_diagnostics] "; describe("SDK failure diagnostics", () => { + it("projects model admission phases, authoritative sequences and policy metadata without content", () => { + const cases = [ + { phase: "received" }, + { phase: "no_tracker" }, + { + phase: "fenced", + event_sequence: 17, + policy_revision: 3, + authority_fenced: false, + preparation_pending: true, + policy_enabled: true, + }, + { phase: "pending", event_sequence: 17 }, + { phase: "submitted", event_sequence: 17, projected_event_count: 1, submitted: true }, + { phase: "submitted", projected_event_count: 0, submitted: false }, + ]; + for (const fields of cases) { + const safe = sanitizeDiagnosticLine( + `${prefix}sdk otel admission ${JSON.stringify({ + ...fields, + session_id: "PRIVATE_SESSION", + event_id: "PRIVATE_EVENT", + model: "PRIVATE_MODEL", + payload: { content: "PRIVATE_PAYLOAD" }, + error: "PRIVATE_ERROR", + path: "PRIVATE_PATH", + })}` + ); + expect(safe).toBeDefined(); + expect(JSON.parse(safe!.slice("sdk otel admission ".length))).toEqual(fields); + expect(safe).not.toContain("PRIVATE"); + } + for (const fields of [ + { phase: "PRIVATE_PHASE" }, + { event_sequence: -1 }, + { event_sequence: 0.5 }, + { event_sequence: Number.MAX_SAFE_INTEGER + 1 }, + { event_sequence: "PRIVATE_SEQUENCE" }, + { event_sequence: null }, + { policy_revision: -1 }, + { policy_revision: 0.5 }, + { projected_event_count: -1 }, + { projected_event_count: 0.5 }, + { authority_fenced: "PRIVATE_STATE" }, + { preparation_pending: null }, + { policy_enabled: "PRIVATE_STATE" }, + { submitted: "PRIVATE_STATE" }, + { session_id: "PRIVATE_SESSION" }, + ]) { + expect( + sanitizeDiagnosticLine(`${prefix}sdk otel admission ${JSON.stringify(fields)}`) + ).toBeUndefined(); + } + }); + + it.each([ + { + label: "native authority fence", + record: "sdk otel policy state", + fields: { + origin: "native_pending", + kind: "fence", + expected_some: false, + previous_incarnation: 2, + previous_revision: 10, + current_incarnation: 2, + current_revision: 11, + previous_authority_fenced: false, + authority_fenced: true, + previous_preparation_pending: true, + preparation_pending: true, + previous_native_owned: true, + native_owned: true, + policy_enabled: true, + returned_some: true, + }, + }, + { + label: "resident credential publication", + record: "sdk otel core policy publication", + fields: { + origin: "standalone_provider_rebind", + kind: "pending_publish_returned", + registry_generation: 1, + previous_auth_generation: 2, + auth_generation: 3, + auth_generation_changed: true, + previous_server_pending: false, + server_pending: true, + policy_identity_present: true, + source_recovery: false, + }, + }, + { + label: "superseded host reservation", + record: "sdk otel policy settlement", + fields: { + origin: "standalone_commit", + kind: "reservation", + succeeded: true, + reservation_some: false, + }, + }, + { + label: "successfully disabled application", + record: "sdk otel policy settlement", + fields: { + origin: "standalone_commit", + kind: "application", + expected_incarnation: 2, + expected_revision: 3, + succeeded: true, + applied: false, + }, + }, + ])("projects $label without credential or policy content", ({ record, fields }) => { + const safe = sanitizeDiagnosticLine( + `${prefix}${record} ${JSON.stringify({ + ...fields, + session_id: "PRIVATE_SESSION", + registration: "PRIVATE_REGISTRATION", + account: "PRIVATE_ACCOUNT", + policy: { endpoint: "PRIVATE_ENDPOINT", headers: { token: "PRIVATE_TOKEN" } }, + error: "PRIVATE_ERROR", + })}` + ); + expect(safe).toBeDefined(); + expect(JSON.parse(safe!.slice(record.length + 1))).toEqual(fields); + expect(safe).not.toContain("PRIVATE"); + }); + + it.each([ + ["sdk otel policy state", "current_revision", "authority_fenced"], + ["sdk otel core policy publication", "auth_generation", "server_pending"], + ["sdk otel policy settlement", "committed_revision", "reservation_some"], + ])("rejects unaudited or invalid %s fields", (record, count, flag) => { + for (const fields of [ + { origin: "PRIVATE_ORIGIN" }, + { kind: "PRIVATE_KIND" }, + ...[-1, 0.5, Number.MAX_SAFE_INTEGER + 1, "PRIVATE_COUNT", null].map((value) => ({ + [count]: value, + })), + { [flag]: "PRIVATE_STATE" }, + { [flag]: null }, + { session_id: "PRIVATE_SESSION" }, + ]) { + expect( + sanitizeDiagnosticLine(`${prefix}${record} ${JSON.stringify(fields)}`) + ).toBeUndefined(); + } + }); + + it("preserves policy attribution through the audited file collector", async () => { + const home = await mkdtemp(join(tmpdir(), "sdk-policy-diagnostics-")); + onTestFinished(() => rm(home, { recursive: true, force: true })); + await mkdir(join(home, "logs")); + await writeFile( + join(home, "logs", "policy.log"), + [ + `${prefix}sdk otel core policy publication {"origin":"standalone_provider_rebind","kind":"pending_publish_returned","auth_generation":3,"account":"PRIVATE"}`, + `${prefix}sdk otel policy state {"origin":"native_pending","kind":"fence","current_revision":11,"authority_fenced":true,"policy":"PRIVATE"}`, + `${prefix}sdk otel policy settlement {"origin":"standalone_commit","kind":"reservation","succeeded":true,"reservation_some":false,"session_id":"PRIVATE"}`, + ].join("\n") + ); + expect(await readDiagnosticTail(home)).toEqual([ + 'sdk otel core policy publication {"origin":"standalone_provider_rebind","kind":"pending_publish_returned","auth_generation":3}', + 'sdk otel policy state {"origin":"native_pending","kind":"fence","current_revision":11,"authority_fenced":true}', + 'sdk otel policy settlement {"origin":"standalone_commit","kind":"reservation","succeeded":true,"reservation_some":false}', + ]); + }); + + it("projects fixed transport teardown phases and bounded counts without SDK content", () => { + for (const phase of [ + "shutdown_writer", + "launcher", + "launcher_complete", + "driver", + "driver_complete", + "driver_failed", + "driver_timeout", + "driver_abort_complete", + "writer_shutdown_complete", + "peer_eof", + "notification_worker", + "notification_worker_complete", + "notification_worker_failed", + "notification_worker_timed_out", + "active_request_grace", + "handle_close", + "handle_close_complete", + "active_requests", + "active_requests_complete", + "active_requests_timed_out", + "flush", + "flush_complete", + "flush_cancelled", + "flush_timed_out", + "flush_unavailable", + "writer_join", + "writer_join_complete", + "complete", + ]) { + const safe = sanitizeDiagnosticLine( + `${prefix}sdk transport teardown ${JSON.stringify({ + connection: 4, + active_requests: 2, + driver_finished: false, + phase, + method: "PRIVATE_METHOD", + request: { token: "PRIVATE_TOKEN" }, + sdkLog: "PRIVATE_LOG", + error: "PRIVATE_ERROR", + })}` + ); + expect(safe).toBeDefined(); + expect(JSON.parse(safe!.slice("sdk transport teardown ".length))).toEqual({ + connection: 4, + active_requests: 2, + driver_finished: false, + phase, + }); + expect(safe).not.toContain("PRIVATE"); + } + for (const fields of [ + { phase: "PRIVATE_PHASE" }, + { active_requests: -1 }, + { active_requests: 0.5 }, + { active_requests: Number.MAX_SAFE_INTEGER + 1 }, + { active_requests: "PRIVATE_COUNT" }, + { connection: -1 }, + { connection: null }, + { driver_finished: "PRIVATE_STATE" }, + { method: "PRIVATE_METHOD" }, + ]) { + expect( + sanitizeDiagnosticLine(`${prefix}sdk transport teardown ${JSON.stringify(fields)}`) + ).toBeUndefined(); + } + expect( + sanitizeDiagnosticLine( + `${prefix}sdk transport teardown {"phase":"complete","active_requests":0}` + ) + ).toBe('sdk transport teardown {"active_requests":0,"phase":"complete"}'); + }); + it("projects only audited fields and never falls back to raw data", () => { expect( sanitizeDiagnosticLine( @@ -26,8 +271,8 @@ describe("SDK failure diagnostics", () => { payload: "PRIVATE_PROMPT", credential: "PRIVATE_CREDENTIAL", endpoint: "PRIVATE_ENDPOINT", - })}`, - ), + })}` + ) ).toBe('sdk pending tool response {"generation":9,"reason":"completed_request"}'); expect( sanitizeDiagnosticLine( @@ -40,10 +285,10 @@ describe("SDK failure diagnostics", () => { origin: "client_response", terminal_cancelled: false, requestId: "PRIVATE_REQUEST", - })}`, - ), + })}` + ) ).toBe( - 'sdk pending transition {"generation":9,"event_count":4,"kind":"permission","origin":"client_response","terminal_cancelled":false}', + 'sdk pending transition {"generation":9,"event_count":4,"kind":"permission","origin":"client_response","terminal_cancelled":false}' ); expect( sanitizeDiagnosticLine( @@ -56,9 +301,11 @@ describe("SDK failure diagnostics", () => { requestId: "PRIVATE_REQUEST", result: "PRIVATE_RESULT", error: "PRIVATE_ERROR", - })}`, - ), - ).toBe('sdk pending tool delivery {"generation":9,"origin":"client_response","accepted":true,"success":true,"failed":false}'); + })}` + ) + ).toBe( + 'sdk pending tool delivery {"generation":9,"origin":"client_response","accepted":true,"success":true,"failed":false}' + ); for (const line of [ `${prefix}PRIVATE_MESSAGE {"generation":9}`, `${prefix}sdk pending tool response {"reason":"PRIVATE_REASON"}`, @@ -86,8 +333,8 @@ describe("SDK failure diagnostics", () => { Array.from( { length: 150 }, (_, generation) => - `${prefix}sdk pending stale drain ${JSON.stringify({ generation, expected_generation: 1, secret: "PRIVATE" })}`, - ).join("\n"), + `${prefix}sdk pending stale drain ${JSON.stringify({ generation, expected_generation: 1, secret: "PRIVATE" })}` + ).join("\n") ); } await writeFile( @@ -96,13 +343,15 @@ describe("SDK failure diagnostics", () => { `${prefix}sdk detach {"phase":"received","connection":2,"dispatch":1,"content":"PRIVATE"}`, `${prefix}sdk detach {"phase":"dispatch_complete","connection":2,"dispatch":1,"failed":false,"elapsed_ms":4}`, '[sdk_diagnostics] sdk detach {"phase":"received","content":"PRIVATE"}', - ].join("\n"), + ].join("\n") ); const tail = await readDiagnosticTail(home); expect(tail).toHaveLength(200); expect(tail[0]).toContain('"generation":102'); expect(tail.at(-2)).toBe('sdk detach {"connection":2,"dispatch":1,"phase":"received"}'); - expect(tail.at(-1)).toBe('sdk detach {"connection":2,"dispatch":1,"elapsed_ms":4,"failed":false,"phase":"dispatch_complete"}'); + expect(tail.at(-1)).toBe( + 'sdk detach {"connection":2,"dispatch":1,"elapsed_ms":4,"failed":false,"phase":"dispatch_complete"}' + ); expect(tail.join("\n")).not.toContain("PRIVATE"); expect(tail.join("\n")).not.toContain(home); }); @@ -237,7 +486,7 @@ describe("SDK failure diagnostics", () => { span: { content: "PRIVATE_SPAN" }, sessionId: "PRIVATE_SESSION", childId: "PRIVATE_CHILD", - })}`, + })}` ); expect(safe).toBeDefined(); expect(JSON.parse(safe!.slice(message.length + 1))).toEqual(fields); @@ -250,7 +499,9 @@ describe("SDK failure diagnostics", () => { { phase: "PRIVATE_PHASE" }, { stream: "PRIVATE_STREAM" }, ]) { - expect(sanitizeDiagnosticLine(`${prefix}sdk shell reader ${JSON.stringify(fields)}`)).toBeUndefined(); + expect( + sanitizeDiagnosticLine(`${prefix}sdk shell reader ${JSON.stringify(fields)}`) + ).toBeUndefined(); } }); }); diff --git a/test/harness/server.ts b/test/harness/server.ts index 887a57178b..f190b2d2f5 100644 --- a/test/harness/server.ts +++ b/test/harness/server.ts @@ -6,11 +6,18 @@ import { ReplayingCapiProxy } from "./replayingCapiProxy"; import { ConnectProxy } from "./connectProxy"; import { createE2eRequestHandler } from "./mockHandlers"; +console.error( + `[SDK proxy startup] Server dependencies loaded at ${Math.round(performance.now())}ms`, +); + // Starts up an instance of the ReplayingCapiProxy server // The intention is for this to be usable in E2E tests across all languages const proxy = new ReplayingCapiProxy("https://api.githubcopilot.com"); const proxyUrl = await proxy.start(); +console.error( + `[SDK proxy startup] Replay server listening at ${Math.round(performance.now())}ms`, +); const blockedHosts: string[] = []; const unhandledRequests: string[] = []; @@ -39,6 +46,9 @@ const connectProxy = new ConnectProxy( }, ); await connectProxy.start(); +console.error( + `[SDK proxy startup] CONNECT server listening at ${Math.round(performance.now())}ms`, +); proxy.onStopRequested = async () => { if (blockedHosts.length || unhandledRequests.length) { diff --git a/test/harness/sessionExpiryMcpServer.test.mjs b/test/harness/sessionExpiryMcpServer.test.mjs index 3a24a708e3..091b218072 100644 --- a/test/harness/sessionExpiryMcpServer.test.mjs +++ b/test/harness/sessionExpiryMcpServer.test.mjs @@ -2,15 +2,19 @@ * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ +import { Agent, request } from "node:http"; import { test } from "vitest"; import { startSessionExpiryMcpServer } from "./test-mcp-session-expiry-server.mjs"; -test("negotiates legacy session semantics even when the client requests modern MCP", async ({ +test("legacy session expiry is independent of HTTP connection lifetime even for modern clients", async ({ expect, onTestFinished, }) => { const server = await startSessionExpiryMcpServer(); onTestFinished(() => server.close()); + const agent = new Agent({ keepAlive: true }); + onTestFinished(() => agent.destroy()); + const probeSockets = new Set(); const initialize = () => fetch(`${server.url}/mcp`, { @@ -29,29 +33,70 @@ test("negotiates legacy session semantics even when the client requests modern M }); const initialized = await initialize(); expect(initialized.status).toBe(200); + expect(initialized.headers.get("connection")).toBe("close"); expect((await initialized.json()).result.protocolVersion).toBe("2025-03-26"); const sessionId = initialized.headers.get("mcp-session-id"); expect(sessionId).toBeTruthy(); - const ping = () => - fetch(`${server.url}/mcp`, { - method: "POST", - headers: { - "content-type": "application/json", - "mcp-session-id": sessionId, - }, - body: JSON.stringify({ jsonrpc: "2.0", id: 2, method: "ping" }), + const ping = async () => { + const response = await new Promise((resolve, reject) => { + const req = request( + `${server.url}/mcp`, + { + agent, + method: "POST", + headers: { + "content-type": "application/json", + "mcp-session-id": sessionId, + }, + }, + (res) => { + const socket = res.socket; + let body = ""; + res.setEncoding("utf8"); + res.on("data", (chunk) => (body += chunk)); + res.on("error", reject); + res.on("end", () => + resolve({ status: res.statusCode, headers: res.headers, socket, body }) + ); + } + ); + req.on("error", reject); + req.end(JSON.stringify({ jsonrpc: "2.0", id: 2, method: "ping" })); }); + expect(response.headers.connection).toBe("close"); + expect(probeSockets.has(response.socket)).toBe(false); + probeSockets.add(response.socket); + return { status: response.status, body: JSON.parse(response.body) }; + }; const alive = await ping(); expect(alive.status).toBe(200); - expect(await alive.json()).toEqual({ jsonrpc: "2.0", id: 2, result: {} }); + expect(alive.body).toEqual({ jsonrpc: "2.0", id: 2, result: {} }); const expired = await fetch(`${server.url}/__expire`, { method: "POST" }); expect(expired.status).toBe(200); expect(await expired.json()).toEqual({ expired: 1 }); - const afterExpiry = await ping(); - expect(afterExpiry.status).toBe(404); - expect(await afterExpiry.json()).toEqual({ error: "session_expired" }); + for (let probe = 0; probe < 3; probe++) { + const afterExpiry = await ping(); + expect(afterExpiry.status).toBe(404); + expect(afterExpiry.body).toEqual({ error: "session_expired" }); + } + const observed = await fetch(`${server.url}/__stats`).then((response) => response.json()); + expect(observed.pings).toBe(1); + expect(observed.expiredRequests).toBe(3); + expect(observed.expiredRequestLog).toEqual( + Array.from({ length: 3 }, () => ({ method: "POST", elapsedMs: expect.any(Number) })), + ); + expect(observed.protocolVersions).toEqual(["2026-07-28"]); + expect(observed.recentRequests).toContainEqual( + expect.objectContaining({ + httpMethod: "POST", + sessionAttached: true, + expiredSession: true, + status: 404, + }), + ); + expect(JSON.stringify(observed)).not.toContain(sessionId); const reinitialized = await initialize(); expect(reinitialized.status).toBe(200); @@ -107,3 +152,40 @@ test("distinguishes answered expiry probes from unrelated traffic", async ({ expect(observed.status).toBe(200); expect(await observed.json()).toMatchObject({ activeSessions: 0, expiredSessionProbes: 1 }); }); + +test("bounds session-expiry diagnostics without retaining request payloads", async ({ expect, onTestFinished }) => { + const startingServer = startSessionExpiryMcpServer(); + onTestFinished(async () => (await startingServer).close()); + const server = await startingServer; + for (let i = 0; i < 40; i++) { + const response = await fetch(`${server.url}/mcp`, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ + jsonrpc: "2.0", + id: `private-request-${i}`, + method: "initialize", + params: { + protocolVersion: "2025-03-26", + clientInfo: { name: "private-client", version: "1" }, + }, + }), + }); + expect(response.status).toBe(200); + await response.json(); + } + const observed = await fetch(`${server.url}/__stats`).then((response) => response.json()); + expect(observed.initializations).toBe(40); + expect(observed.recentRequests).toHaveLength(32); + for (const request of observed.recentRequests) { + expect(request).toEqual({ + elapsedMs: expect.any(Number), + httpMethod: "POST", + sessionAttached: false, + expiredSession: false, + rpcMethods: ["initialize"], + status: 200, + }); + } + expect(JSON.stringify(observed)).not.toContain("private-"); +}); diff --git a/test/harness/startup.mjs b/test/harness/startup.mjs new file mode 100644 index 0000000000..38f2540b57 --- /dev/null +++ b/test/harness/startup.mjs @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +console.error( + `[SDK proxy startup] Node entered at ${Math.round(performance.now())}ms`, +); +await import("tsx"); +console.error( + `[SDK proxy startup] TypeScript loader ready at ${Math.round(performance.now())}ms`, +); +await import("./server.ts"); diff --git a/test/harness/startup.test.ts b/test/harness/startup.test.ts new file mode 100644 index 0000000000..d3ada9ee3b --- /dev/null +++ b/test/harness/startup.test.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { execFile } from "node:child_process"; +import { mkdtemp, rm, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; +import { promisify } from "node:util"; +import { test } from "vitest"; + +const execute = promisify(execFile); +const bootstrap = fileURLToPath(new URL("./startup.mjs", import.meta.url)); + +test.for(["tsx", "./server.ts"])( + "records the last startup phase before import %s fails", + async (failedImport, { expect, onTestFinished, signal }) => { + const creatingDirectory = mkdtemp( + path.join(os.tmpdir(), "sdk-proxy-startup-"), + ); + let executing: ReturnType | undefined; + onTestFinished(async () => { + const [created] = await Promise.allSettled([creatingDirectory]); + try { + if (executing) await Promise.allSettled([executing]); + } finally { + if (created.status === "fulfilled") { + await rm(created.value, { recursive: true, force: true }); + } + } + }); + const directory = await creatingDirectory; + signal.throwIfAborted(); + const loader = path.join(directory, "loader.mjs"); + const registration = path.join(directory, "register.mjs"); + await writeFile( + loader, + `export function resolve(specifier, context, nextResolve) { + if (specifier === ${JSON.stringify(failedImport)}) throw new Error("CONTROLLED_IMPORT_FAILURE"); + if (specifier === "tsx") return { url: "data:text/javascript,export%20%7B%7D", shortCircuit: true }; + return nextResolve(specifier, context); + }`, + ); + await writeFile( + registration, + `import { register } from "node:module"; + register(${JSON.stringify(pathToFileURL(loader).href)}, import.meta.url);`, + ); + + executing = execute( + process.execPath, + ["--import", pathToFileURL(registration).href, bootstrap], + { + signal, + env: { ...process.env, NODE_OPTIONS: "" }, + }, + ); + await expect(executing).rejects.toMatchObject({ + code: 1, + stdout: "", + stderr: expect.stringMatching( + /\[SDK proxy startup\] Node entered.*CONTROLLED_IMPORT_FAILURE/su, + ), + }); + await expect(executing).rejects.toMatchObject({ + stderr: + failedImport === "tsx" + ? expect.not.stringContaining("TypeScript loader ready") + : expect.stringContaining("TypeScript loader ready"), + }); + }, +); diff --git a/test/harness/stdio-shutdown-runtime.cjs b/test/harness/stdio-shutdown-runtime.cjs index 154537a3c0..f3230a041d 100644 --- a/test/harness/stdio-shutdown-runtime.cjs +++ b/test/harness/stdio-shutdown-runtime.cjs @@ -9,13 +9,23 @@ const { dirname } = require("node:path"); const [marker, mode, pidFile] = process.argv.slice(2); const useTcp = process.argv.includes("--fixture-tcp"); -if (!marker || !pidFile || !["stop", "dispose", "force", "fallback", "start-failure", "shutdown-error"].includes(mode)) { +if (!marker || !pidFile || !["stop", "dispose", "force", "fallback", "start-failure", "shutdown-error", "connect-stall", "port-stall"].includes(mode)) { throw new Error("Expected cleanup marker, shutdown mode, and PID file arguments"); } fs.writeFileSync(pidFile, String(process.pid)); -if (mode === "fallback" || mode === "start-failure") { +if (mode === "fallback" || mode === "start-failure" || mode === "connect-stall" || mode === "port-stall") { setInterval(() => {}, 1000); } +if (process.argv.includes("--fixture-descendant")) { + const child = require("node:child_process").spawn( + process.execPath, + ["-e", "setInterval(() => {}, 1000)"], + { stdio: "ignore" }, + ); + child.on("error", (error) => { throw error; }); + fs.writeFileSync(`${pidFile}.descendant`, String(child.pid)); + child.unref(); +} let buffered = Buffer.alloc(0); let shutdownRequested = false; @@ -35,6 +45,7 @@ const handleData = (chunk, output) => { let error; switch (message.method) { case "connect": + if (mode === "connect-stall") continue; result = { ok: true, protocolVersion: mode === "start-failure" ? -1 : 3, @@ -42,7 +53,7 @@ const handleData = (chunk, output) => { }; break; case "ping": - result = { message: message.params?.message, timestamp: Date.now(), protocolVersion: 3 }; + result = { message: message.params?.message, timestamp: new Date().toISOString(), protocolVersion: 3 }; break; case "runtime.shutdown": shutdownRequested = true; @@ -68,7 +79,7 @@ if (useTcp) { socket.on("data", (chunk) => handleData(chunk, socket)); }); server.listen(0, "127.0.0.1", () => { - console.log(`listening on port ${server.address().port}`); + if (mode !== "port-stall") console.log(`listening on port ${server.address().port}`); }); } else { process.stdin.on("data", (chunk) => handleData(chunk, process.stdout)); diff --git a/test/harness/test-mcp-computer-use-oauth-server.mjs b/test/harness/test-mcp-computer-use-oauth-server.mjs new file mode 100644 index 0000000000..2b8dc3c82f --- /dev/null +++ b/test/harness/test-mcp-computer-use-oauth-server.mjs @@ -0,0 +1,173 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import http from "node:http"; + +export async function startComputerUseOAuthServer({ + deferRegistration = false, + deferInitialChallenge = false, + deferAuthorizationMetadata = false, + preconfiguredClientId = "", +} = {}) { + let clientId = preconfiguredClientId; + let releaseRegistration = () => {}; + const registration = deferRegistration + ? new Promise((resolve) => { + releaseRegistration = resolve; + }) + : Promise.resolve(); + let releaseInitialChallenge = () => {}; + const initialChallenge = deferInitialChallenge + ? new Promise((resolve) => { + releaseInitialChallenge = resolve; + }) + : Promise.resolve(); + let releaseAuthorizationMetadata = () => {}; + const authorizationMetadata = deferAuthorizationMetadata + ? new Promise((resolve) => { + releaseAuthorizationMetadata = resolve; + }) + : Promise.resolve(); + const requests = []; + const token = "computer-use-isolation-token"; + const server = http.createServer(async (request, response) => { + const url = new URL(request.url ?? "/", `http://${request.headers.host}`); + const chunks = []; + for await (const chunk of request) chunks.push(chunk); + const body = Buffer.concat(chunks).toString("utf8"); + requests.push({ + method: request.method, + path: url.pathname, + authorization: request.headers.authorization ?? null, + body, + }); + const respond = (status, value, headers = {}) => { + response.writeHead(status, { "content-type": "application/json", ...headers }); + response.end(JSON.stringify(value)); + }; + if (request.method === "GET" && url.pathname === "/.well-known/oauth-protected-resource") { + respond(200, { + resource: `${url.origin}/mcp`, + authorization_servers: [url.origin], + scopes_supported: ["mcp.read"], + bearer_methods_supported: ["header"], + }); + return; + } + if (request.method === "GET" && url.pathname === "/.well-known/oauth-authorization-server") { + await authorizationMetadata; + respond(200, { + issuer: url.origin, + authorization_endpoint: `${url.origin}/authorize`, + token_endpoint: `${url.origin}/token`, + registration_endpoint: `${url.origin}/register`, + response_types_supported: ["code"], + grant_types_supported: ["authorization_code"], + }); + return; + } + if (request.method === "POST" && url.pathname === "/register") { + await registration; + clientId = "registered-client"; + respond(201, { client_id: clientId, client_id_issued_at: Math.floor(Date.now() / 1000) }); + return; + } + if (request.method === "POST" && url.pathname === "/token") { + const form = new URLSearchParams(body); + if (!clientId || form.get("client_id") !== clientId) { + respond(400, { error: "invalid_client" }); + return; + } + if ( + form.get("grant_type") !== "authorization_code" || + form.get("code") !== "accepted-code" || + !form.get("code_verifier") + ) { + respond(400, { error: "invalid_grant" }); + return; + } + respond(200, { access_token: token, token_type: "Bearer", expires_in: 3600 }); + return; + } + if (url.pathname !== "/mcp") { + respond(404, { error: "not_found" }); + return; + } + if (request.headers.authorization !== `Bearer ${token}`) { + await initialChallenge; + respond( + 401, + { error: "unauthorized" }, + { + "www-authenticate": `Bearer resource_metadata="${url.origin}/.well-known/oauth-protected-resource", scope="mcp.read"`, + }, + ); + return; + } + if (request.method !== "POST") { + respond(405, { error: "method_not_allowed" }); + return; + } + let message; + try { + message = JSON.parse(body); + } catch { + respond(400, { error: "invalid_json" }); + return; + } + if (message.id === undefined) { + response.writeHead(202); + response.end(); + return; + } + let result; + if (message.method === "initialize") { + result = { + protocolVersion: "2025-03-26", + capabilities: { tools: {} }, + serverInfo: { name: "computer-use-oauth-isolation", version: "1.0.0" }, + }; + } else if (message.method === "tools/list") { + result = { + tools: [ + { + name: "whoami", + description: "Reports an authenticated connection", + inputSchema: { type: "object", properties: {}, additionalProperties: false }, + }, + ], + }; + } else if (message.method === "ping") { + result = {}; + } else { + respond(200, { jsonrpc: "2.0", id: message.id, error: { code: -32601, message: "Method not found" } }); + return; + } + respond(200, { jsonrpc: "2.0", id: message.id, result }, { "mcp-session-id": "computer-use-oauth-isolation" }); + }); + await new Promise((resolve, reject) => { + server.once("error", reject); + server.listen(0, "127.0.0.1", () => { + server.off("error", reject); + resolve(); + }); + }); + const address = server.address(); + if (!address || typeof address === "string") throw new Error("Expected TCP server address"); + return { + url: `http://127.0.0.1:${address.port}`, + requests, + releaseRegistration, + releaseInitialChallenge, + releaseAuthorizationMetadata, + close: () => + new Promise((resolve, reject) => { + releaseRegistration(); + releaseInitialChallenge(); + releaseAuthorizationMetadata(); + server.close((error) => (error ? reject(error) : resolve())); + server.closeAllConnections(); + }), + }; +} diff --git a/test/harness/test-mcp-server.mjs b/test/harness/test-mcp-server.mjs index dfc791e127..a1c3609767 100644 --- a/test/harness/test-mcp-server.mjs +++ b/test/harness/test-mcp-server.mjs @@ -33,6 +33,7 @@ const startupMarkerPath = getArgument("--startup-marker"); const diagnosticStderr = getArgument("--diagnostic-stderr"); const serverName = getArgument("--server-name") ?? "env-echo"; const toolName = getArgument("--tool-name") ?? "get_env"; +const toolDescriptionFile = getArgument("--tool-description-file"); const server = new McpServer({ name: serverName, version: "1.0.0" }); const fixtures = JSON.parse(await readFile(new URL("./mcp-prompt-fixtures.json", import.meta.url), "utf8")); // Fixtures contain SDK extension bags; MCP sends those entries as ordinary object fields. @@ -98,7 +99,9 @@ server.server.setRequestHandler(GetPromptRequestSchema, async ({ params }) => { server.tool( toolName, - "Returns the value of the specified environment variable.", + toolDescriptionFile + ? await readFile(toolDescriptionFile, "utf8") + : "Returns the value of the specified environment variable.", { name: z.string().describe("Environment variable name") }, async ({ name }) => ({ content: [{ type: "text", text: process.env[name] ?? "" }], diff --git a/test/harness/test-mcp-session-expiry-server.mjs b/test/harness/test-mcp-session-expiry-server.mjs index a28343b151..4751be0bfd 100644 --- a/test/harness/test-mcp-session-expiry-server.mjs +++ b/test/harness/test-mcp-session-expiry-server.mjs @@ -16,6 +16,8 @@ * server-side idle expiry is observed by a real Streamable HTTP client. * `GET /__stats` reports the counters tests assert on, including answered * expired-session probes so recovery assertions need not budget the idle delay. + * HTTP connections close after each response so socket idle retirement cannot + * interfere with the independently managed MCP session lifetime. */ import http from "node:http"; @@ -24,6 +26,7 @@ import { randomUUID } from "node:crypto"; import { fileURLToPath } from "node:url"; const PROTOCOL_VERSION = "2025-03-26"; +const MAX_RECENT_REQUESTS = 32; export async function startSessionExpiryMcpServer({ host = "127.0.0.1", port = 0 } = {}) { const activeSessions = new Set(); @@ -39,12 +42,16 @@ export async function startSessionExpiryMcpServer({ host = "127.0.0.1", port = 0 protocolVersions: [], expiredSessionProbes: 0, }; + const recentRequests = []; + const startedAt = performance.now(); const server = http.createServer(async (req, res) => { + // MCP session expiry is independent of TCP keep-alive expiry. + res.setHeader("connection", "close"); const url = new URL(req.url ?? "/", `http://${req.headers.host ?? `${host}:${port}`}`); if (req.method === "GET" && url.pathname === "/__stats") { - respondJson(res, 200, { ...stats, activeSessions: activeSessions.size }); + respondJson(res, 200, { ...stats, activeSessions: activeSessions.size, recentRequests }); return; } @@ -64,7 +71,21 @@ export async function startSessionExpiryMcpServer({ host = "127.0.0.1", port = 0 } const sessionId = req.headers["mcp-session-id"]; - if (typeof sessionId === "string" && expiredSessions.has(sessionId)) { + const expiredSession = typeof sessionId === "string" && expiredSessions.has(sessionId); + const observation = { + elapsedMs: Math.round(performance.now() - startedAt), + httpMethod: req.method, + sessionAttached: typeof sessionId === "string", + expiredSession, + rpcMethods: [], + status: null, + }; + recentRequests.push(observation); + if (recentRequests.length > MAX_RECENT_REQUESTS) recentRequests.shift(); + res.once("finish", () => { + observation.status = res.statusCode; + }); + if (expiredSession) { stats.expiredRequests++; if (stats.expiredRequestLog.length < 8) { stats.expiredRequestLog.push({ @@ -99,6 +120,10 @@ export async function startSessionExpiryMcpServer({ host = "127.0.0.1", port = 0 } const messages = Array.isArray(parsedBody.value) ? parsedBody.value : [parsedBody.value]; + observation.rpcMethods = messages + .map((message) => message?.method) + .filter((method) => typeof method === "string") + .slice(0, 4); const isInitialize = messages.some((message) => message?.method === "initialize"); let responseSessionId = typeof sessionId === "string" ? sessionId : undefined; diff --git a/test/snapshots/permissions/should_handle_async_permission_handler_dotnet.yaml b/test/snapshots/permissions/should_handle_async_permission_handler_dotnet.yaml new file mode 100644 index 0000000000..08ee2fa0f2 --- /dev/null +++ b/test/snapshots/permissions/should_handle_async_permission_handler_dotnet.yaml @@ -0,0 +1,20 @@ +models: + - claude-sonnet-5 +conversations: + - messages: + - role: system + content: ${system} + - role: user + content: Call async_permission_echo once and reply with only its returned text. + - role: assistant + tool_calls: + - id: toolcall_0 + type: function + function: + name: async_permission_echo + arguments: "{}" + - role: tool + tool_call_id: toolcall_0 + content: DOTNET_ASYNC_PERMISSION_RESULT + - role: assistant + content: DOTNET_ASYNC_PERMISSION_RESULT diff --git a/test/snapshots/rpc_session_state/should_read_the_reasoning_summaries_policy_for_the_current_model.yaml b/test/snapshots/rpc_session_state/should_read_the_reasoning_summaries_policy_for_the_current_model.yaml new file mode 100644 index 0000000000..0c6b353c19 --- /dev/null +++ b/test/snapshots/rpc_session_state/should_read_the_reasoning_summaries_policy_for_the_current_model.yaml @@ -0,0 +1,3 @@ +models: + - claude-sonnet-5 +conversations: [] diff --git a/test/snapshots/rpc_tasks_and_handlers/should_emit_session_idle_after_cancelling_a_running_attached_shell.yaml b/test/snapshots/rpc_tasks_and_handlers/should_emit_session_idle_after_cancelling_a_running_attached_shell.yaml new file mode 100644 index 0000000000..d22c9aefea --- /dev/null +++ b/test/snapshots/rpc_tasks_and_handlers/should_emit_session_idle_after_cancelling_a_running_attached_shell.yaml @@ -0,0 +1,21 @@ +models: + - claude-sonnet-5 +conversations: + - messages: + - role: system + content: ${system} + - role: user + content: Use your shell tool to run `sleep 600` in async mode without detaching. Do not read its output or wait for it. + Then reply with exactly SHELL_STARTED. + - role: assistant + tool_calls: + - id: toolcall_0 + type: function + function: + name: ${shell} + arguments: '{"command":"sleep 600","description":"Sleep for 600 seconds","mode":"async"}' + - role: tool + tool_call_id: toolcall_0 + content: "" + - role: assistant + content: SHELL_STARTED diff --git a/test/snapshots/rpc_tasks_and_handlers/should_run_a_queued_message_after_cancelling_a_running_attached_shell.yaml b/test/snapshots/rpc_tasks_and_handlers/should_run_a_queued_message_after_cancelling_a_running_attached_shell.yaml new file mode 100644 index 0000000000..d2dbce4e77 --- /dev/null +++ b/test/snapshots/rpc_tasks_and_handlers/should_run_a_queued_message_after_cancelling_a_running_attached_shell.yaml @@ -0,0 +1,25 @@ +models: + - claude-sonnet-5 +conversations: + - messages: + - role: system + content: ${system} + - role: user + content: Use your shell tool to run `sleep 600` in async mode without detaching. Do not read its output or wait for it. + Then reply with exactly SHELL_STARTED. + - role: assistant + tool_calls: + - id: toolcall_0 + type: function + function: + name: ${shell} + arguments: '{"command":"sleep 600","description":"Sleep for 600 seconds","mode":"async","initial_wait":10}' + - role: tool + tool_call_id: toolcall_0 + content: "" + - role: assistant + content: SHELL_STARTED + - role: user + content: Reply with exactly QUEUED_DONE. + - role: assistant + content: QUEUED_DONE diff --git a/test/snapshots/skill_provider/should_preserve_skill_prompt_bytes_across_cold_resume_with_reordered_discovery.yaml b/test/snapshots/skill_provider/should_preserve_skill_prompt_bytes_across_cold_resume_with_reordered_discovery.yaml new file mode 100644 index 0000000000..72910c73cd --- /dev/null +++ b/test/snapshots/skill_provider/should_preserve_skill_prompt_bytes_across_cold_resume_with_reordered_discovery.yaml @@ -0,0 +1,14 @@ +models: + - claude-sonnet-5 +conversations: + - messages: + - role: system + content: ${system} + - role: user + content: Without using tools, reply with exactly SKILL_ORDER_INITIAL_OK. + - role: assistant + content: SKILL_ORDER_INITIAL_OK + - role: user + content: Without using tools, reply with exactly SKILL_ORDER_RESUMED_OK. + - role: assistant + content: SKILL_ORDER_RESUMED_OK